Group Details Private

Global Moderators

Forum wide moderators

  • RE: How to set the active documents preview image

    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.

    54c636b2-05c8-4e3a-af41-9edf7e80bd24-image.png

    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.

    3e4681a7-7db6-45b7-a1da-5279cc028cf2-image.png

    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:

    459e7ff6-a8fe-4abf-ae35-adc803c17170-image.png

    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

    posted in Cinema 4D SDK
  • RE: Python reading fields issue

    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 make even less gurantees. 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

    posted in Bugs
  • RE: Mesh Cleanup - script or plugin for C4D?

    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. Therefor I moved your topic to the General Talk which is the place to make request to the community. While Cinema 4D SDK board is purely about development questions.

    • 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: Asking Questions.

    About your First Question

    There is no such plugins (at least that I'm aware) in Cinema 4D, however you may be able to find issue in your mesh via the Mesh Checking features available in the Mode of the Attribute Manager.

    1d38a2e5-3304-415b-803b-bf6859b55c34-image.png

    I let the community answers regarding your request in case a developer have done a plugins that may fit your needs.
    Cheers,
    Maxime.

    posted in General Talk
  • RE: Python reading fields issue

    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: Asking Questions.

    About your First Question

    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

    posted in Bugs
  • RE: RenderDocument produces different color

    Hey @Gregor-M,

    Thank you for reaching out to us. As a general note: Generally you should not continue such old threads, as this is usually counter productive. But in this case this is fine, since your question is just a repetition of the OT's question.

    I currently do not have that much time, but the issue is related to OCIO. At least that was the case in the past. A BaseBitmap carries color profiles for the principal OCIO color spaces such as 'Display' or 'View Transform'. RenderDocument did not correctly setup these profiles, or more precisely something with copying over these profiles to the final result went wrong. But that should have been fixed, but the recent OCIO changes might have introduced a regression. There is not much what you can do and I will earliest have time next week.

    I had a quick look, and when you enable "Scene" as the configuration, the bitmap will look just like the native one. That is because it will then use the display space and view transform of the scene and not the ones attached to the bitmap (so this indeed seems to be the old issue).
    8f701195-a918-4c84-a829-59a12cf65e64-image.png

    I would say this just a cosmetic effect (an admittedly annoying one), but your actual saved bitmap should look the same when you open it in Photoshop or a similar app. Will have a look in a week!

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to Swap location in Object Manager

    Hey @pchg,

    Thank you for reaching out to us. There is no dedicated "Swap" function for node hierarchies. All the relevant tools can be found on cinema::GeListNode. The general drill would be here to either get the node before or after the nodes you want to swap, remove the nodes, and then use GeListNode::InsertBefore or InsertAfter to insert the nodes. Please note that other than in the Python API, there is no fail safe which prevents you from inserting nodes multiple times into a scene graph (which is not allowed and will lead to crashes).

    Cheeers,
    Ferdinand

    Code

    I wrote this 'blind' in a text editor, i.e., this is uncompiled code to demonstrate the principle:

    BaseObject* const a = static_cast<BaseObject*>(selection->GetIndex(count - obj_10));
    BaseObject* const b = static_cast<BaseObject*>(selection->GetIndex(count - obj_2));
    if (!a || !b)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "a or b is nullptr"_s);
    
    // Get the predecessor of the objects a and b which can be a null pointer when either a or b is the 
    // first object in the hierarchy. We could then use either the next object as the insertion point or
    // use GeListNode::InsertUnder or BaseDocument::InsertObject to insert the object at the top of its
    // hierarchy local hierarchy. I went here for the latter as for the next object you also have to deal
    // with the case that there is neither a predecessor nor a next object.
    BaseObject* const predA = a->GetPred();
    BaseObject* const predB = b->GetPred();
    BaseObject* const parentA = a->GetUp();
    BaseObject* const parentB = b->GetUp();
    
    // Remove both objects from the hierarchy.
    a->Remove();
    b->Remove();
    
    // Insert the objects back into the hierarchy, either after the predecessor, under the parent, 
    // or at the top of the document hierarchy.
    if (predA) 
      b->InsertAfter(predA);
    else if (parentA)
      b->InsertUnder(parentA); // Will insert at the top of the hierarchy.
    else
      doc->InsertObject(b, nullptr, nullptr); // Will insert at the top of the hierarchy.
    
    if (predB)
      a->InsertAfter(predB);
    else if (parentB)
      a->InsertUnder(parentB);
    else
      doc->InsertObject(a, nullptr, nullptr);
    
    posted in Cinema 4D SDK
  • RE: TempUVHandle always None! Why?

    Hi @ThomasB,

    Please note that R21 is generally out of scope of full support on this forum. I've tried your code with the oldest C4D version I have (which is R25) and I cannot reproduce your issue.

    However, looking in your code it seems like you might need an additional handling for the case when the texture view is not open, please have a look at the comment in our example: call_uv_command_r18.py .

    You can also find some useful information on a similar thread, where Ferdinand gave some insights about GetActiveUVSet function: GetActiveUVSet() returns None if multiple UVs are active?.

    Cheers,
    Ilia

    posted in Cinema 4D SDK
  • RE: Loop tool and knife tool are useless in R2025, revert back to R23

    Okay, we are going to end this thread here, as this discussion leads nowhere. Please contact end user support about this.

    posted in General Talk
  • RE: Monitoring object parameter changes

    Hey @Márton,

    It depends a bit on what you are doing concretely. There could be a case where you want to stream hyper-complex scene data between the scene representations of two apps: E.g., particles or maybe some super heavy CAD or sculpting data. And you want your app to be the new standard for "man, this is snappy" where you might have to roll up your sleeves and optimize what you copy over from the Cinema 4D graph to yours and vice versa.

    But generally I would avoid assuming that caches always have the same layout or having to identify things in a cache (at the cost of a bit of performance) and treat caches as monolithic things instead. And yes, then GeMarker/MAXON_CREATOR_ID are by far the best option. GeMarker is a hash of the mac address of the machine, the timestamp of creation, and a special secret sauce. Markers can be copied on copy events with C4DAtom::CopyTo when PRIVATE_IDENTMARKER is passed as a flag to maintain the identity of nodes (which will be done by Cinema 4D when it 'reallocates' nodes, shoves them around in the backend).

    Effectively this means that GeMarker/MAXON_CREATOR_ID is persistent over node reallocation, scene loading/unloading, and scene duplication events (a node will have the same marker in the document the user is using for editing as in the cloned version of that document used in a rendering running in the background while the user is editing). Or spoken very plainly: They will (almost) never change.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to obtain the color of each ball in each frame

    Hello @pchg,

    Thank you for reaching out to us. Please note that we normally expect question to be accompanied by code. It is also still unanswered in which context you want to do this: As an effector, as a Script Manager script, etc.

    Cheers,
    Ferdinand

    Result

    347fc2f0-a0cd-4583-816b-b3e38fda2280-image.png

    Code

    """Reads out the particle data of a MoGraph Cloner object.
    
    Must be run as a Script Manager script in Cinema 4D with a MoGraph Cloner object selected.
    """
    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 op or not op.CheckType(c4d.Omgcloner):
            raise ValueError("Please select a MoGraph Cloner object.")
        
        # Get the particle data from the cloner object.
        data: c4d.modules.mograph.MoData = c4d.modules.mograph.GeGetMoData(op)
        if data is None:
            return
        
        # Get the transform and color data for the particles.
        matrices: list[c4d.Matrix] = data.GetArray(c4d.MODATA_MATRIX)
        colors: list[c4d.Vector] = data.GetArray(c4d.MODATA_COLOR)
    
        # Iterate over all particle data and do something with it. If we were in an effector, we could
        # also write back data. Technically, we could also write back data here, but it would be volatile.
        for i in range(data.GetCount()):
            matrix: c4d.Matrix = matrices[i]
            color: c4d.Vector = colors[i]
            print(f"Particle {i} at {matrix.off} with color {color}")
    
    
    if __name__ == '__main__':
        main()
    

    And here is the 2024.0.0 effector full control mode default code which more or less is very similar but also writes data.

    """Realizes an effector that attracts MoGraph particles spherically around its origin.
    
    Add the example to a Matrix object to understand its effect. In Full Control mode we can realize
    a true attraction force as we have full control over the particle values. Compare this example to
    Parameter Control mode to understand the difference.
    """
    import c4d
    
    op: c4d.BaseObject # The Python Effector object containing this code.
    gen: c4d.BaseObject # The MoGraph Generator executing `op`.
    doc: c4d.documents.BaseDocument # The document `op` and `gen`are contained in.
    
    def main() -> bool:
        """Called by Cinema 4D to evaluate the effector.
        """
        # Get the particle data for the effector #op. Get out when either the data cannot be retrieved.
        data: c4d.modules.mograph.MoData = c4d.modules.mograph.GeGetMoData(op)
        if data is None:
            return 1.0
    
        # The transform of both the generator and the effector, the transforms of all particles, and
        # finally the position of the effector as if it would live in the coordinate system of the
        # generator.
        mgGen: c4d.Matrix = gen.GetMg()
        mgEff: c4d.Matrix = op.GetMg()
        matrices: list[c4d.Matrix] = data.GetArray(c4d.MODATA_MATRIX)
        q: c4d.Vector = ~mgGen * mgEff.off
    
        # For each particle compute a weight `w` for how much the particle should be attracted to the
        # attraction point `q`, and then blend the particle position between the attraction point and
        # its own position `p`.
        for i in range(data.GetCount()) :
            p: c4d.Vector = matrices[i].off
            w: float = c4d.utils.RangeMap((mgGen * ~mgEff * p).GetLength(), 0., 100., 0., 1., True) ** 3.
            matrices[i].off = c4d.utils.MixVec(q, p, w)
    
        # Write the new data back.
        data.SetArray(c4d.MODATA_MATRIX, matrices, op[c4d.FIELDS].HasContent())
        return True
    
    posted in Cinema 4D SDK