Group Details Private

administrators

  • RE: Tree view boolean column bug

    Hi @Gregor-M please make sure that your script is executable before sending it to us. Since in it's current state it does raise

    Traceback (most recent call last):
      File "scriptmanager", line 53, in GetDown
      File "scriptmanager", line 33, in GetChildren
    AttributeError: 'Entity' object has no attribute 'children'. Did you mean: 'GetChildren'?
    

    I added self.children = [] within the __init__ of Entity and it fixed the issue but this indicate that you are working on a different version than us. But even with it I'm not able to reproduce any issue and everything is working as expected.

    With that's said your else statement within the SetCheck looks very suspicious and I won't be surprised that the behavior you are experiencing is coming from this else statement.

    Finally note that you can call the function TreeViewCustomGui.SetRoot to define a root. Then the root argument that is available in all functions will be pointing to it.

    Cheers,
    Maxime.

    posted in Cinema 4D SDK
  • RE: Docked Dialog Problem with width of CUSTOMGUI_FILENAME

    Hi @datamilch,

    Although I highly suspect this to be a bug, I'll need more time to double check it. I'll update you further, once have a more clear vision on this.

    Cheers,
    Ilia

    posted in Cinema 4D SDK
  • RE: Change Icon Color parameter

    Hi @chuanzhen,

    The "Display Color" value of the "Icon Color" attribute is a dynamic value (comparing to "None" and "Custom" being static values), this was explained in the related thread: Python Documentation - Icon Color. This is the reason why it requires special handling, namely (as Manuel explained in the related thread: Simple Organisational Structure Generation Script), one needs to send a c4d.MSG_GETCUSTOMICON_SETTINGS message to the object to properly set this attribute. Please check the example code snippet that shows its usage.

    Cheers,
    Ilia

    Example code, showing the usage of the c4d.MSG_GETCUSTOMICON_SETTINGS message:

    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:
        obj = c4d.BaseObject(c4d.Ojoint)
        
        # Configure object display color attribute
        obj[c4d.ID_BASEOBJECT_USECOLOR] = c4d.ID_BASEOBJECT_USECOLOR_ALWAYS
        obj[c4d.ID_BASEOBJECT_COLOR] = c4d.Vector(1, 0, 1)
    
        # Configure object icon color attribute
        settings = c4d.CustomIconSettings()
        settings._colorMode = 2
        obj.Message(c4d.MSG_GETCUSTOMICON_SETTINGS, {'setting': settings})
        obj[c4d.ID_BASELIST_ICON_COLORIZE_MODE] = c4d.ID_BASELIST_ICON_COLORIZE_MODE_CUSTOM + 1
    
        obj.SetName("Magenta Joint")
        doc.InsertObject(obj)
    
    
    if __name__ == '__main__':
        main()
        c4d.EventAdd()
    
    posted in Cinema 4D SDK
  • RE: Issue: CommandData Plugin Compilation Errors in Cinema 4D 2025.1.0 SDK

    Hey @skibinvitaliy,

    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

    No one is judging you here, problems are part of the game and what we are here for to help with 🙂 . But please familiarize yourself with our forum rules, such question should be accompanied by example code and the version you are migrating from, as we otherwise mostly have to guess what is going wrong.

    I am assuming you are migrating from a pre-2025 SDK, that is at least what your errors imply. With 2025 we introduced our cinema namespace. Please follow our Migrating Plugins to the 2025.0 API guide. You are likely just missing a couple of using namespace cinema; at the top of your files.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: Python Tag plugin : Is it possible to disable(ghost) the host object's parameters?

    Hey @ymoon,

    thank you for reaching out to us. While you can access the description of a scene element at any time via c4d.C4DAtom.GetDesccription, it will always be static, i.e., read only there. Only in the context of NodeData.GetDEnabling and NodeData.GetDDescription, the description of a node will be dynamic (that is what the 'D' stands for in these methods), i.e., writeable.

    It is therefore only possible to runtime modify the description of nodes you do implement yourself. Otherwise the system would inevitably produce race conditions. Where a parameter X of an object O being shown or not, would depend on how two objects A (wants to show O.X) and B (wants to hide O.X) are placed in relation to O in the object manager; and which of (A, B) is therefore executed first and which last.

    There is one exception to that rule, and this is user data, accessible via GetUserDataContainer (Python) and C4DAtom::GetDynamicDescription(Writeable) (C++), here you can always modify the description. There is however no direct flag in the description to disable an element, the closest thing you can do, is hide an element with DESC_HIDE.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: Hiding Node Previews for all Redshift Nodes in Redshift Materials

    Hey,

    there is no NOT in the query syntax, if that is what you are asking for, although that could be a cool idea. What you would have to do, is apply two partial descriptions in one ApplyDescription call, one that first selects all nodes with an enabled preview and disables it, and then one which selects the end node and disables its preview. Or just carry out two successive ApplyDescription calls.

    Cheers,
    Ferdinand

    PS: You can of course do whatever you want with your module attributes, and things like __author__, __license__, __email__, etc are not formally standardized, but the quasi standard module attribute is called __author__, there is no __authors__. When you have multiple authors, you usually put them semicolon separated into the __author__ field. When you use __authors__, tools which support that quasi standard won't find your attribute and the whole purpose of these module attributes is being machine readable, as you could otherwise also just write things into the doc string of the module.

    See:
    The original Python Enhancement Proposal by Guido van Rossum (the now famous pep8) which introduced the concept of module dunder attributes and one of the many guides of how people interpret this, what this has evolved into.

    posted in Cinema 4D SDK
  • RE: Hiding Node Previews for all Redshift Nodes in Redshift Materials

    Hey @d_keith,

    Thank you for your question. Please remember our forum rules, which prohibit diary style postings. I understand that you did this under the banner of being documentative, but too much noise in a posting can confuse future readers.

    From Support Procedures: How to Ask Questions:

    Singular Posting: Users often discover additional information or even a solution before we can answer. Please consolidate your questions into a singular posting by editing your last posting. It is otherwise not only for us but also for future readers hard to follow the topic. Once we replied to a topic you are of course then free to reply in a new posting. But when you have follow-up questions, all information should again be consolidated in the most recent posting until we answer.

    The preview of a material graph node is just an attribute with the ID net.maxon.node.base.preview. You would have to iterate over all nodes in a graph and set it to False (just as in @Dunhou's maxon example, but you could condense there some code using maxon.GraphDescription.GetMaterialGraphs). You can use @Dunhou's Nodes API wrapper which provides some nice abstractions for users who do not want to go too deep into the Nodes API, but at the same time stay within the domain of it.

    An alternative would be Graph Descriptions, specifically their graph query feature which is more aimed at the technical complexity artists are usually more comfortable with. I have provided an example below.

    Cheers,
    Ferdinand

    PS: I just realized that there is a typo in one of the graph query code examples in the manual, it should be of course QUERY_FLAGS.MATCH_MAYBE and not QUERY_FLAGS.MATCH_MATCH_MAYBE, will fix that 🙂

    Result

    The state of a document with three very simple Redshift materials after the graph description has been bulk applied, all nodes have a collapsed preview:
    b4b54b82-ab97-400a-85ff-0e3fc0031912-image.png

    Code

    """Provides a simple example for a graph description query to modify all nodes in all Redshift 
    material graphs in a document which have a set of specific properties, here at the example of 
    the "Show Preview" property.
    """
    
    import c4d
    import maxon
    
    doc: c4d.documents.BaseDocument # The currently active document.
    
    def main() -> None:
        """
        """
        # For each material in the document which has a Redshift material node space, get its graph and ...
        for graph in maxon.GraphDescription.GetMaterialGraphs(doc, maxon.NodeSpaceIdentifiers.RedshiftMaterial):
            # ... apply a graph description to the graph, that ...
            maxon.GraphDescription.ApplyDescription(
                graph, {
                    # ... defines a query for the passed graph that can match many to none nodes ...
                    "$query": {
                        "$qmode": (maxon.GraphDescription.QUERY_FLAGS.MATCH_ALL | 
                                   maxon.GraphDescription.QUERY_FLAGS.MATCH_MAYBE),
                        # ... where each matched node has the following properties (we could add here
                        # more properties) ...
                        "Basic/Show Preview": True # Node has its preview enabled.
                    },
                    # ... so that we can then write into each node new properties, here we simply disable
                    # the preview for all matched nodes.
                    "Basic/Show Preview": False
                }
            )
        
        # Push an update event (not really necessary in this case, but good practice at the end of scripts)
        c4d.EventAdd()
            
    if __name__=='__main__':
        main()
    
    posted in Cinema 4D SDK
  • RE: Automating ColorSpace Change for Textures in Redshift

    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

    With that's said we are not a coding service, but we offer help to people to learn how to use the Cinema 4D SDK. So in the future please show us that you tried a bit at least and where are you blocked so we can help you. Let's cut your question in two.

    First one, "How to change the ColorSpace" of all materials. You can find an answers in this topic: SetPortValue for Texture node ColorSpace. However this affect only the new Redshift Node Material. If you want to support the old one (xpresso based node material) let me know. You can retrieve all materials using BaseDocument.GetMaterials.

    Then second one, "How to restore value", there is no magic here you need to store the value somewhere. It depends on the behavior you are expecting, if you want to support this ability even if you close and re-open the file. The best way would be store this data in the BaseContainer of the material you modified. A BaseContainer is like a Python Dictionary but it's a Cinema 4D that is attached to any objects/tags/material in Cinema 4D. Any information written into it is then saved in the c4d file. The drawback is that not all datatype are supported.

    So with that's said here a possible example on how to achieve what you want:

    """
    This script automates the process of changing the ColorSpace of all textures in a Cinema 4D project, specifically targeting new Redshift Node materials. 
    
    It performs the following tasks:
    
    1. Iterates through all materials in the active document and identifies Redshift materials.
    2. For each Redshift material, retrieves the current ColorSpace values of texture nodes (e.g., RAW, sRGB).
    3. Changes the ColorSpace of the texture nodes to a predefined target (TARGET_COLOR_SPACE, default "ACEScg").
    4. Serializes and stores the original ColorSpace values in a `BaseContainer` attached to the material, ensuring persistence across sessions.
    5. If needed, restores the original ColorSpace values if they were previously saved, allowing for easy reversion.
    6. The process is fully automated with a single execution, and the changes are saved to the material for future use.
    """
    import c4d
    import maxon
    
    doc: c4d.documents.BaseDocument  # The currently active document.
    op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
    
    # Be sure to use a unique ID obtained from https://developers.maxon.net/forum/pid
    UNIQUE_BC_ID = 1000000
    NODESIDS_BC_ID = 0
    VALUES_BC_ID = 1
    TARGET_COLOR_SPACE = "ACEScg"
    
    def GetDictFromMat(mat: c4d.Material) -> dict:
        """
        Retrieves a dictionary containing the current ColorSpace values of a material.
        The ColorSpace values are serialized into a `BaseContainer` and stored on the material,
        ensuring that the data persists even after the file is closed and reopened. This is done
        because node IDs are unique for each material graph, so the serialized data is specific 
        to the material and its associated graph.
    
        Args:
            mat (c4d.Material): The material object whose ColorSpace values are to be retrieved.
    
        Returns:
            dict: A dictionary where keys are node IDs (unique to the material's graph) and values 
                  are the corresponding ColorSpace values.
                  The dictionary is constructed from data stored in the material's `BaseContainer`, 
                  which persists across sessions.
        """
        matBC: c4d.BaseContainer = mat.GetDataInstance()
    
        personalBC: c4d.BaseContainer = matBC.GetContainerInstance(UNIQUE_BC_ID)
        if personalBC is None:
            return {}
    
        nodeIdsBC: c4d.BaseContainer = personalBC.GetContainerInstance(NODESIDS_BC_ID)
        nodeValuesBC: c4d.BaseContainer = personalBC.GetContainerInstance(VALUES_BC_ID)
        if nodeIdsBC is None or nodeValuesBC is None:
            return {}
    
        dct: dict = {}
        for index, _ in enumerate(nodeIdsBC):
            nodeId = nodeIdsBC[index]
            colorSpaceValue = nodeValuesBC[index]
            dct[nodeId] = colorSpaceValue
    
        return dct
    
    def SaveDictToMat(mat: c4d.Material, dct: DictType) -> None:
        """
        Saves the ColorSpace dictionary back to the material, ensuring that the data is serialized 
        into a `BaseContainer` and stored on the material. This serialized data will persist across
        sessions, so the original ColorSpace values can be restored even after the file is closed and reopened.
        Since node IDs are unique for each material's graph, the data is saved in a way that is specific 
        to that material and its texture nodes.
    
        Args:
            mat (c4d.Material): The material object to which the dictionary will be saved.
            dct (dict): A dictionary where keys are node IDs (unique to the material's graph) and values are 
                        the corresponding ColorSpace values. This dictionary will be serialized and saved 
                        in the material's `BaseContainer`.
        """
        matBC: c4d.BaseContainer = mat.GetDataInstance()
    
        # Create containers for node IDs and their corresponding ColorSpace values
        nodeIdsBC = c4d.BaseContainer()
        nodeValuesBC = c4d.BaseContainer()
    
        # Populate the containers with the node IDs and ColorSpace values from the dictionary
        for index, key in enumerate(dct.keys()):
            nodeIdsBC[index] = key
            nodeValuesBC[index] = dct[key]
    
        # Create a personal container to hold the node IDs and values
        personalBC = c4d.BaseContainer()
        personalBC[NODESIDS_BC_ID] = nodeIdsBC
        personalBC[VALUES_BC_ID] = nodeValuesBC
    
        # Attach the personal container to the material's data container
        matBC[UNIQUE_BC_ID] = personalBC
    
        # Set the material's data with the updated container, ensuring the ColorSpace values are stored
        mat.SetData(matBC)
    
    
    def main() -> None:
        """
        Main function to automate changing the ColorSpace of all textures in the project.
        It changes the ColorSpace to TARGET_COLOR_SPACE and stores the previous values in the BaseContainer
        of the material, a kind of dictionary but in Cinema 4D format so it persists across sessions.
        This allows for reverting the ColorSpace values back to their original state, even after closing and reopening the file.
        """
        # Loop through all materials in the document
        for material in doc.GetMaterials():
            # Ensure the material has a Redshift node material
            nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
            if not nodeMaterial.HasSpace(maxon.NodeSpaceIdentifiers.RedshiftMaterial):
                continue
    
            # Retrieve the Redshift material graph
            graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
                material, maxon.NodeSpaceIdentifiers.RedshiftMaterial)
    
            # Retrieve the existing ColorSpace values as a dictionary from the material
            matDict: dict = GetDictFromMat(material)
    
            # Begin a graph transaction to modify nodes
            with graph.BeginTransaction() as transaction:
                # Define the asset ID for texture nodes in the Redshift material
                rsTextureAssetId = "com.redshift3d.redshift4c4d.nodes.core.texturesampler"
    
                # Loop through all the texture nodes in the material graph
                for rsTextureNode in maxon.GraphModelHelper.FindNodesByAssetId(graph, rsTextureAssetId, True):
                    # Find the ColorSpace input port for each texture node
                    colorSpacePort: maxon.GraphNode = rsTextureNode.GetInputs().FindChild(
                        "com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0").FindChild("colorspace")
                    
                    # Skip the node if the ColorSpace port is invalid
                    if not colorSpacePort or not colorSpacePort.IsValid():
                        continue
    
                    # Get the path which is unique for each graph for this ColorSpace port
                    colorSpacePortId = str(colorSpacePort.GetPath())
    
                    # If there's an existing ColorSpace value, restore it to the previous state
                    if colorSpacePortId in matDict:
                        colorSpacePort.SetPortValue(matDict[colorSpacePortId])
    
                         # Remove the entry from the dictionary after restoring,
                         # so next time the TARGET_COLOR_SPACE will be used
                        del matDict[colorSpacePortId] 
                    else:
                        # Otherwise, store the current ColorSpace value and set the new target ColorSpace
                        previousValue = colorSpacePort.GetPortValue()
                        if previousValue.IsNullValue():
                            previousValue = ""
    
                        matDict[colorSpacePortId] = str(previousValue)
                        colorSpacePort.SetPortValue(TARGET_COLOR_SPACE)
    
                # Commit the transaction to apply the changes to the material
                transaction.Commit()
    
            # After modifying the ColorSpace values, save the updated values back to the material
            SaveDictToMat(material, matDict)
    
            # Refresh the scene to ensure the changes are applied
            c4d.EventAdd()
    
    # Run the main function when the script is executed
    if __name__ == '__main__':
        main()
    

    Cheers,
    Maxime.

    posted in Cinema 4D SDK
  • RE: How to Activate Polygon Selection in the Selection Filter

    Hey @seora,

    Thank you for reaching out to us. Selection filter modes are just commands. Since there is no GUI involved with most of them, you can just 'record' your actions with the script log and then 'play' that back. Just to be clear, you can also invoke commands that entail GUI, e.g., the "Selector .." command, but you cannot automate that GUI (as GUIs are generally not exposed in the API).

    e27953f9-0c1c-4dce-a1bb-b4d643aebacf-image.png

    """A simple script that disables all selection filters, and then just enables the polygons filter.
    """
    from typing import Optional
    import c4d
    
    doc: c4d.documents.BaseDocument # The active document
    op: Optional[c4d.BaseObject] # The active object, None if unselected
    
    def main() -> None:
        """Called by Cinema 4D when the script is executed.
        """
        c4d.CallCommand(69000, 902) # Set selection filter to None.
        c4d.CallCommand(69000, 904) # Enable Polygon filter.
    
    if __name__ == '__main__':
        main()
        c4d.EventAdd()
    

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to convert the length of a line on the screen to the length of an object.

    Hey,

    Yes, parallel/orthographic projection is effectively just the dot product. The modelling examples cover point plane projections with a function very similar to yours. There is also c4d.utils.PointLineSegmentDistance but I never use it, as it strikes me as more cumbersome to use than just doing the math yourself.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK