Hey @Gemini,
as I just said, there aren't any, even in the non-public C++ API. When you want to provide a layout, you must save it as a layout file and then load it via LoadFile
.
Cheers,
Ferdinand
Hey @Gemini,
as I just said, there aren't any, even in the non-public C++ API. When you want to provide a layout, you must save it as a layout file and then load it via LoadFile
.
Cheers,
Ferdinand
Hey @ECHekman,
COLORA and VECTOR4D are both Maxon datatypes. For how to deal with custom data types, you can have a look at this. But since this is a maxon data type and not a cinema data type, you cannot use a static_cast. When you do not want to store your maxon data wrapped as data, the only thing which remains is unsafely casting the data (but our style guide recommends using reinterpret_cast
to make the unsafe nature obvious).
const maxon::ColorA* myColor = reinterpret_cast<const maxon::ColorA*>(geData.GetCustomDataTypeI(DTYPE_COLORA));
I had a look and we do this in some UI related places too, so this might be a side effect of the 2024 changes to data types in relation to that specific custom gui.
Cheers,
Ferdinand
Hey,
I appreciate you separating questions But there is code for this exact question in the old thread:
https://developers.maxon.net/forum/topic/15823/data-datatypes-python-and-gui/2.
You must wrap the Data
as GeData
. One can of course play pointer games, as with pretty much everything, Cinema API types are usually just Maxon API types in disguise, but I would not recommend that. GeData
, i.e., a BaseContainer
, cannot store Data
directly.
Cheers,
Ferdinand
Hey @Gemini,
Thank you for reaching out to us. Please have a look at Programmatically create a palette?. The TLDR is: There is no API for interacting with palettes, even internally we do not dynamically inject things into palettes (except for a few special cases) and instead rely on layout files.
Cheers,
Ferdinand
Hey @ECHekman,
you are right, IntVector32
has been wrapped by Maxime, my bad. Regarding being a new specialization or not, I guess that depends a bit on the perspective. IntVector32
is an alias for a specialization of Vec3
.
And what I was hinting at, is that when you define your own specialization, as I did above for my bool vector example BoolVec3
, the Python API won't automatically know how to deal with it (although it knows what a Vec3
is). But you can wrap a data type relatively easily with pure Python code yourself (check out {C4D}\resource\modules\python\libs\python311\maxon
and the files vec
, vec4
, and vector
). To then use that type you would have to either put that binding into each script, import the binding from a module in each script, or make it so that your plugin puts it into \python311\_maxon_init
so that it is automatically exposed in the maxon package (please be careful with patching a users __init__.py
, as it could be customized, so you cannot blindly overwrite that file).
Regarding the rest, I already lined out your options above. Maybe I am still misunderstanding you, but you cannot use a Maxon datatype as a parameter, nor can you implement your own Cinema datatype and have the Python API understand it (1). As lined out above, the best option for you is likely writing a custom GUI which emulates 2, 3, or 4 component integer vector type and sits on top of the existing data type Vector4D.
Regarding your concerns about precsion: The error in a Float64
(the underlying component type of Vector
/DYTPE_VECTOR
) becomes larger than 1 when the value exceeds 2^53
. That is outside of the interval [-2^31, 2^31 - 1]
of Int32
.
Regarding the 'UI working for maxon::IntVector32': I can only speculate what you are doing there, my assumption would be that you probably have some form of dynamic description and shove that IntVector32
into the data container of the node. Feel free to expore this route, but that is not supported. (Cinema API) Descriptions must be composed out of Cinema API datatypes. The interpolation issues you encounter are like tied to how CKey::Set/GetValue
and CKey::Set/GetData
work and the API then getting confused about the datatype in the data container of the scene element which does not conform to a registered DTYPE_
(or on point, it likely treats your values as data although they are values).
To stress it gain, a custom GUI which wraps arround Vector4D
is porbably what you want to do here. If this is not a fit, please share concrete code and a concrete usage scneario, as we won't get anywhere otherwise.
Cheers,
Ferdinand
(1) With the exception of data types which are decomposable into channels, e.g., what DTYPE_VECTOR
does and many datatypes in Redshift do. So, you would then end up with a custom datatype DTYPE_FOO
and the Python API would still have no clue what to with it when the user calls myNode[c4d.FOO_PARAMETER]
and then raise an error. But when your data type is decomposable into known types (three DTYPE_LONG
in your case), the user could call myNode[c4d.FOO_PARAMETER, c4d.VECTOR_X]
to directly access the x-component of it.
Hello @Gregor-M,
when you have a problem with bit depths, please open a new thread. This is now a bug thread and we should keep it clean.
What I forgot to mention here is that there is one way to sort of fix this right now for you. There is an undocumented flag named RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER
on the render data (not the same as RDATA_BAKE_OCIO_VIEW_TRANSFORM
which is exposed in the UI tto the user). With that you can bake the full transform chain into the bitmap. Such bitmap will then be displayed correctly in the PV. But when you save it to disk, it will be "incorrect", as it got the view and display transform baked into it (which is usually not what you want).
But it could be an option if you only volatiely render documents for displaying purposes only or are okay with rendering things twice (once with the flag for displaying, and once without it for saving). But that is of course very much a hack why we will fix this.
Cheers,
Ferdinand
Hey @Mats,
Thank you for reaching out to us. Is it intentional that you posted in General Talk? Because this strikes me more as an API question; although very broad in scope, which might be why you posted here.
What to use depends a bit on your goals. Graph descriptions are currently most geared towards creating new graphs from scratch. But with graph queries you can also modify existing graphs. Graph descriptions are still actively being developed, for 2025.0.0, when the docs only contained the change note about having fixed varidaic port handling I actually implemented much more, but that is currently all undocumented.
Documenting this is in my backlog, and planned for the upcoming release. Generally speaking graph descriptions are also not a new way, and the Nodes API an old way. Graph descriptions are just a more streamlined way to interact with nodes graphs (something like handling variadic ports or the undocumented node commands can either be quite complicated or outright impossible in the Python Nodes API).
But generally remains the fact that the Nodes API is the 'true' API which always will be more powerful (at least to the extent it has been wrapped for Python). My suggestion would be either to ask a concrete question what you want to do, or wait for the 2025.1 docs which are not far away. I will then have hopfeully found the time to document the new features and add some examples.
Cheers,
Ferdinand
Hey @T1001,
yes, you are on the right track there, to initialize an image as RGBA F32, you would do something like this:
static const Int32 size = 1024;
const ImageRef image = ImageClasses::IMAGE().Create() iferr_return;
const maxon::ColorProfile profile = ColorSpaces::RGBspace().GetDefaultNonlinearColorProfile();
const PixelFormat format = PixelFormats::RGBA::F32();
const Int32 channelCount = format.GetChannelCount();
// Init the bitmap with its size, storage convention, and pixel format. Whe choose here the
// "normal", i.e., consecutive layout. An alternative would be a planar layout.
image.Init(size, size, ImagePixelStorageClasses::Normal(), format) iferr_return;
image.Set(IMAGEPROPERTIES::IMAGE::COLORPROFILE, profile) iferr_return;
Regarding the rest, I roughly cover this in my example. A little GeUserArea which wraps around some render buffer data. I rather not yet share it here, as it "misbehaves" still in one aspect, and I am not sure if that is my or the API's fault (will have to catch one of the Image API devs for that).
You can also have a look at the Color Management and OCIO manual as I covered there quite a bit of pixel format and buffer handling subjects. Last but not least regarding hardware acceleration: The Image API does not have much drawing functions, that is primarily done in the semi-public Drawport API. It depends a bit on what you want to accelerate, filling a buffer cannot be accelerated, you either fill things row by row, or just wrap around some existing memory.
When you need hardware accelerated drawing functions like drawing a circle, etc., then you indeed must use the Drawport API. To get access to it as an external, you would have to apply for Maxon Registered Developer membership, as this would also grant access to the semi-public Drawport API. As its name implies, the Drawport API is the API with wich viewports are drawn in Cinema 4D. You might be familar with BaseDraw
, the Cinema API type which is used to draw into view ports. Similar to how you can get an ImageRef
from a BaseBitmap
, you can get a DrawPortRef
from a BaseDraw
. Under the hood, draw ports are also used to draw into bitmaps, and there is also a mechanism with which one can attach a draw port to a dialog (a draw port user area).
But not all parts of the Drawport API are semi-public, some are fully private. I would first have to check, if the bitmap drawing part is semi-public and I know for a fact that the user area is not semi-public (but we wanted to expose it at some point but I think it never happend).
So, long story short, when you think you need all that, I would recommend reaching out via our contact form or directly apply for registered developer membership.
Cheers,
Ferdinand
Hello @ECHekman,
please read my answer I have given above.
- You can store pretty much anything in a
BaseContainer
since you can store there raw memory. Because you can storemaxon::Data
, this also means you can store a wide range of Maxon API data types without getting hacky with raw memory.- The Python API understands
maxon::Data
and knows things likemaxon::Vec3
, but you when you define a new template specialization, it obviously won't know what to do with that. Stuff likeIntVector32
is not wrapped for Python.- You usually only need a custom data type and GUI when you want to use that data type as a parameter in a node. E.g., have a shader which has a parameter Enabled which is of type
BoolVector3
. When you implement a new custom data type, you must always implement a custom GUI (but a new custom GUI can sit on top of an existing data type).- Python won't know what to do with that data type. When you try call
MyNode.GetParameter()
on that parameter, Python will throw an error.Bottom line is here, you cannot implement a new data type and have the Python API magically understand it. You could:
- Ship you own Python bindings for that data type.
- Just implement a custom GUI which makes a Vector4d act like an integer 2, 3, or 4 vector. (That is in fact what the Vector2D custom gui is doing with Vector).
You neither can just have magically a GUI manifest for some data type, nor can you use maxon::Data
as a parameter type directly. As I stated in my previous answer, when you want to use your new data type(s) as parameter(s) types - opposed to just storing them in the data container of the node - you will have to implement a custom data type and GUI for them. But the Python API will then not be able to deal with such data types (unless you make them decomposable into existing data types - which would be possible in your case).
Cheers,
Ferdinand
Hello @ECHekman,
Thank you for reaching out to us. Please have a look at Support Procedures: Asking Questions. We understand that it can be difficult to limit yourself to a clear singular question, but we must enforce that, as it becomes otherwise very hard to answer subjects. Your posting violates the singular and clear question rule.
When I read here a bit between the lines, and distill this down to the main question that you want to store vectors of different size and base type in a BaseContainer
, your approach of implementing this yourself is possible but unnecessary I would say. You also do not tell us where you vector types come from, because while I do understand implicitly their purpose, they are AFAIK not part of std
.
When you do not want to use cinema::Vector
and ::Vector4d
, you could just use the templates Vec2, Vec3, and Vec4 to just define the vector types you need. There are also plenty using
aliases which predefine common cases. cinema::Vector
and ::Vector4d
are also alias for templates.
Such vector can then then be inserted as maxon::Data
in a BaseContainer. In the end all three of your cases are sort of the same, although there exist some extra methods on
BaseContainerand
GeData` to retrieve some types more easily.
Which leaves us with
I would like to add new data types to the BaseContainer/GeData for a ShaderData and I'm wondering what the best method for storing, displaying, animating and python interop.
BaseContainer
since you can store there raw memory. Because you can store maxon::Data
, this also means you can store a wide range of Maxon API data types without getting hacky with raw memory.maxon::Data
and knows things like maxon::Vec3
, but you when you define a new template specialization, it obviously won't know what to do with that. Stuff like IntVector32
is not wrapped for Python.BoolVector3
. When you implment a new custom data type, you must always implement a custom GUI (but a new custom GUI can sit on top of an existing data type).MyNode.GetParameter()
on that parameter, Python will throw an error.Bottom line is here, you cannot implement a new data type and have the Python API magically understand it. You could:
Cheers,
Ferdinand
#include "maxon/datatypebase.h"
maxon::Result<void> Foo()
{
iferr_scope;
// A BaseContainer can hold native types as Vector or String, but also generic data in the form
// of GeData (we can also write raw memory, but I am ignoring this here). GeData can wrap Data
// which is its Maxon API counter part. And Data can wrap all things that derive from it, which
// includes the vector templates.
// Define a custom three component boolean vector (which is of type maxon::Data).
using BoolVec3 = maxon::Vec3<maxon::Bool, 1>;
BoolVec3 input (true, true, false);
// Write it into a container ...
cinema::BaseContainer bc;
bc.SetData(0, cinema::GeData(maxon::Data(input)));
// ... and read it back.
const maxon::Data& data = bc.GetData(0).GetData();
BoolVec3 output = data.Get<BoolVec3>() iferr_return;
// When we wanted to do manual error handling for the data not wrapping the data we
// think it would wrap, we could do this:
//
// if (data.GetType() != GetDataType<BoolVec3>()) { ... }
ApplicationOutput("Output: @", output);
// Output: (true,true,false)
return maxon::OK;
}
Hey @T1001,
yes, you can do this, converting between pixel formats or use different pixel formats. I will post here a code example in the course of this week which highlights doing what you want to do. As a warning (as I already wrote most of the example but won't have time the next days to finish it), cinema::GeUserArea::DrawImageRef
is currently misssing the enum maxon::IMAGEINTERPOLATIONMODE
in the public API. You can of course just define it yourself but you (currently) won't find it in the public API.
Cheers,
Ferdinand
Hello @T1001,
Thank your for reaching out to us and for splitting your questions into multiple topics. But let's put this topic on hold here until we have asked your other question (which I set as the main question).
Cheers,
Ferdinand
Welcome to the Maxon developers forum and its community, it is great to have you with us!
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.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
Hey, you did a great job for your first your topic(s), I assume you did read our forum rules - thanks. But you should really avoid having more than one question in a topic, because things tend to become very hard to answer then and our answers very long (as evident by my answer here). Try to find a singular main question you have. You can then optionally ask thematically closely related follow-up questions once the main question has been answered.
Before we go here into details, I would recommend having a look at the topic Overlapping images with transparency with BaseBitmap as it is quite tangential to what you seem to be trying to do.
The general problem is that this "writing multiple layers with embedded alpha values" scenario is not something the Cinema Image API (BaseBitmap
, MultipassBitmap
) has been really geared for. The major use case for creating multiple layers with alpha values in the Cinema API, Bodypaint, is covered by cinema::PaintBitmap. But that is sort of its own eco-system. BaseBitmap
and MultipassBitmap
are meant for loading and displaying data.
auto rootAlpha = Bmp->AddAlpha(nullptr, COLORMODE::GRAYf);
but never write to it. Probably in the assumption that this would happen automatically, but it does not.BaseBitmap
and MultipassBitmap
are just a shallow wrapper for the underlying Maxon Image API.BaseBitmap
and the underlying ImageInterface
(which you usually encounter as an ImageRef
in code) support floating point image data. They are defined under the Graphics label as maxon:Pix...
MPBTYPE_SAVE
to false
though).This topic is riddled a bit by too many questions. Let's try to take a more direct angle here and just try to solve what you are trying to do. I assume you want to have some form of UI that stacks multiple images with transparent areas, similar to what the Asset Browser is doing here to display overlay icons over its assets.
Hey @Gregor-M,
so, had a look, and it is mostly as I said.
The actual bitmaps saved to disk are the same in your case and correct. The reason why your script produces bitmaps that will look differently in Photoshop or an external image viewer, is because you set the BaseBitmap::COLORPROFILE_INDEX_IMAGE profile to a linear sRGB profile. A manually started rendering will there usually use sRGB2.1. If you wanted to do this correctly, you would have to take the image color profile from the render settings (but since the user will usually have the default value there, this does not really make a difference in most cases):
rd: c4d.BaseContainer = doc.GetActiveRenderData().GetClone().GetData()
bmp: c4d.bitmaps.BaseBitmap = c4d.bitmaps.BaseBitmap()
bmp.Init(int(rd[c4d.RDATA_XRES]), int(rd[c4d.RDATA_YRES]), 32)
bmp.SetColorProfile(rd[c4d.RDATA_IMAGECOLORPROFILE]])
But bitmaps have more than one color profile since OCIO was introduced, they also have a profile for the render space, view transform, and display space. In Python, you cannot yet set these from code yet, as BaseBitmap.Get/SetProfile
lacks the index argument there, and you also cannot access these profiles in the document.
After some deep dive into our render pipeline, we found out that this profile data is still incorrectly being copied, the bitmap never gets the correct view transform and display sapce profile assigned.
Finally, to reiterate:
ShowBitmap
.RenderDocument
) as the core issue lies deeper within the rendering pipeline.Cheers,
Ferdinand
I would heavily recommend using the extension, as it will automatically curate the paths for you. I.e., when you use the extension and connect to a Cinema 4D instance, it will make sure that the dummy modules of that Cinema 4D version are on the search paths.
But when you really do not want to use it, you can also just edit your config so that the dummy module paths are discoverable for auto complete and the linter.
What you will need in any case, is the Python and Pylance extension for VS Code, as they are the extensions which make use of these settings. When you install the connector, they will be installed automatically as a dependency.
Cheers,
Ferdinand
Hey @j_vogel,
thank you for reaching out to us. Yes, that is how you do it. Please remember to consolidate your posting in the future (you should edit your initial posting until we have answered).
Cheers,
Ferdinand
Hey @MMayrh,
Thank you for reaching out to us. This is not how this parameter works. The parameter DOCUMENT_PREVIEW_IMAGE is of data type BitmapButtonStruct
, not of BaseBitmap
.
The documentation is a bit misleading here. This is not the document preview bitmap, but sort of the preview bitmap delegate. I.e., an entity that is used to retrieve a preview bitmap (for a document in this case). A BitmapButtonStruct
wraps a node, an ID, and a dirty flag.
This parameter does not make too much sense in the Python API, here is what the C++ API does when the parameter is access for a document:
I.e., it returns itself (the doc), the ID of the parameter (DOCUMENT_PREVIEW_IMAGE
) and the dirty state of its internal current preview bitmap as the BitmapButtonStruct
bbs. What you could technically try, is implement a node, e.g., an object, and then set that object as the preview provider for a document. Your node would for that have to implement MSG_DESCRIPTION_GETBITMAP
, because that is what effectively will be called. But that is all very theoretical, DOCUMENT_PREVIEW_IMAGE
is largely unused in our code base, and I do not see any implemnation for the SetParameter
part. So, the document will likely just ignore you trying to overwrite its preview provider. There could be some base implemenation kicking in, but I doubt it.
But what you definitely cannot do, is just set there a bitmap to overwrite the preview image of the document (assuming that was what you wanted to do). That would also not make too much sense since a document is constantly recalculating its preview image. So, on the next update that image would be gone (if it would work like that).
Cheers,
Ferdinand
Yes, we will treat it as a bug. Apparently we implemented it, but then someone disabled the implementation (probably because it caused performance issues or something like that), and then we forgot do follow up on that disabled implementation. Generally we cannot give ETA's for fixes but we try to do them in a timely fashion, i.e., within a couple of minor releases such as 2024.1, 2024.2, 2024.3, etc. I am not the dev here, so I can give even less guarantees. We will update this thread when something blocks us from fixing this in the near future.
And yes, it does work in C++. The reason why this is not working in Python is because of the C++ API changes with 2024.0. This link shows how you sample things in C++, the changes revolved around making sampling a field a const operation, i.e., an operation which does not change data on the object. Which was before not the case and now requires that mutable data to be manually passed arround as the extraData
as shown in the example. The changes were carried out to speed up the core of Cinema 4D.
Cheers,
Ferdinand
Welcome to the Maxon developers forum and its community, it is great to have you with us!
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.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
I can confirm that there is an issue, and there is little that you can do. This goes back to the 2024 API update and how Field sampling changed then in the C++ API. In Python, we postponed things:
Fields sampling have been reworked, however due to time constraint the next methods have not been ported and are not working, we are going to fix it in an upcoming release.
But that apparently somehow got stuck in a backlog and we never picked up on it again. I have moved this topic to the bugs section, there is nothing you can do at the moment.
Cheers,
Ferdinand