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.
Global Moderators
Forum wide moderators
Posts
-
RE: How to draw svg to bitmaps?
-
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,
BaseBitmapis 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. ABaseBitmapwraps internally anImageInterface, 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 codebmp.InitWith(preview_url)does not work, is because the backend function::InitFromFilewhich implementsBaseBitmap::InitWithoperates based on bitmap loaders andVectorImageInterfaceis 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.FromVectorImagemethod into the Python API. For now, you would have to use an external library such as Pillow.Cheers,
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,
FerdinandI.e., your script will then be as follows where there is an
rin 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 ) -
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).


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 -
RE: Can we draw alpha image in viewport
I do not think that
DRAW_ALPHA_FROM_IMAGEshould not work. I just pickedDRAW_ALPHA_NORMALbecause it is the "most default one" out of theDRAW_ALPHAflags. I will have a look later. But you can for now probably just useDRAW_ALPHA_NORMAL. -
RE: Can we draw alpha image in viewport
Hey @Dunhou,
Can you please share code and possibly a bug report? Alphas with
DrawTexturehave a troubled history but when I tried in the py-ocio_node_2025 example right now, it works for me.Cheers,
FerdinandResult
We draw the little cube icon with alphas next to the existing drawing examples.

Code
I just added the highlighted code in the
py-ocio_node_2025example.

# 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.
-
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.zipfor performance reasons and is unpacked on demand. You can just unpack theresource.zipinto you resource folder without an performance or stability issues.
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 wordQUICKTAB.Cheers,
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.

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.
-
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
DescriptionToolDatahooks, 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).
DescriptionToolDatadoes 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 ofDescriptionToolDatabut I am personally not a big fan of implementing sculpting tools in such hacky manner as non-sculpting tools, just to get the benefits ofDescriptionToolData.Cheers,
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