Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. paulgolter
    3. Topics
    P
    • Profile
    • Following 0
    • Followers 0
    • Topics 3
    • Posts 12
    • Best 0
    • Controversial 0
    • Groups 0

    Topics created by paulgolter

    • P

      Python: Cinema exit event

      Cinema 4D SDK
      • python • • paulgolter
      4
      0
      Votes
      4
      Posts
      582
      Views

      ferdinandF

      I think this can be closed 🙂

    • P

      Python: PluginMessage: Execute callback on plugin register and unregister event

      Cinema 4D SDK
      • python • • paulgolter
      7
      0
      Votes
      7
      Posts
      966
      Views

      P

      Hey @ferdinand

      I still do not 100% understand why this entails you having to poke around in the boot sequence of Cinema 4D. You should just register your plugin normally (there is no alternative to that)

      I think it's mainly because I didn't know what the proper way of doing it was. Reading the docs I thought I needed to use c4d.C4DPL_INIT or c4d.C4DPL_STARTACTIVITY to register my plugin stuff / execute my own startup code. I know there are the c4d plugin examples, but maybe it would be nice to add the "proper" way of registering a plugin to the docs.

      Python plugins cannot be disabled in Cinema 4D. You can reload them, but not disable them. You also cannot unregister a plugin.

      Aha also some good information that I was not aware of!

      And to the server:
      I am not running a server inside of cinema. It is running on some other machine in the internet. I am only connecting to it via a httpx.AsyncClient client. For this reason and some other technical reasons the client needs to run in a separate thread, but all of that works fine and we are even using the c4d.C4DThread for this.

      But you answered my question of this thread already, thanks 🙂

    • P

      Python Api: Document Events

      Cinema 4D SDK
      • python • • paulgolter
      8
      0
      Votes
      8
      Posts
      1.3k
      Views

      ferdinandF

      Hey @paulgolter,

      yes, this is okay. What you should not do, is traverse the whole scene graph, i.e., every object, shader, material, scene hook, track, curve, key etc that are to be found in a scene; just to get hold of all cube objects for example. For performance critical operations, we should always only traverse what we really need . But you just traverse the objects which is totally fine.

      I personally would avoid recursion, passing around a list, and not using an iterator, as this all makes things slower. Especially recursion/function calls are not cheap in Python. For the next major release we added some premade scene traversal functions in Python. I have attached one of the functions which will be exposed below.

      But your function is probably fine too, this is all very nitpicky. You do not use full recursion which is the most important thing. Everything else is probably nerd-talk.

      Cheers,
      Ferdinand

      def IterateTree(node: c4d.GeListNode | None, yieldSiblings: bool = False, rewindToFirstSibling: bool = False, rewindToRoot: bool = False) -> typing.Iterator[c4d.GeListNode]: """Yields nodes that are hierarchically related to `node`. .. note:: This function walks a node tree in a purely iterative manner, which makes it fast and robust for medium to large trees. For small trees (<= 100 nodes) `RecurseTree` is slightly faster. This function is usually the faster and safer choice compared to `RecurseTree` as the performance gain for large trees is significant while the loss for small trees is negligible. .. note:: To also yield non-hierarchical relationships, use `RecurseGraph` instead. :param node: The root node to start iterating from. :type node: c4d.GeListNode or None :param yieldSiblings: Whether to yield the next siblings of the current node, defaults to `False`. :type yieldSiblings: bool, optional :param rewindToFirstSibling: Whether to rewind the node to its first sibling before the iteration is carried out, defaults to `False`. :type rewindToFirstSibling: bool, optional :param rewindToRoot: Whether to rewind the node to its root before the iteration is carried out, defaults to `False`. :type rewindToRoot: bool, optional :return: An iterator that yields the descendants of the passed `node`. :rtype: typing.Iterator[c4d.GeListNode] :raises RuntimeError: If the UUID of a node could not be retrieved. :Example: .. code-block:: python import c4d from mxutils import CheckType, IterateTree # For the object tree: # # A # |___B # | |___C # | |___D # | | |___E # | |___F # G # Find the object named "D" in the scene. d: c4d.BaseObject = CheckType(doc.FindObject("D")) # Will yield D and E. for node in IterateTree(d): print (node) # Will yield D, E, and F. But not C as we start out at D and #yieldSiblings only looks # downwards, at next siblings and not previous ones. for node in IterateTree(d, yieldSiblings=True): print (node) # Will yield C, D, E, and F. Because we rewind to the first sibling of D - which is C. for node in IterateTree(d, yieldSiblings=True, rewindToFirstSibling=True): print (node) # Will yield A, B, C, D, E, and F. But not G, as we do not yield siblings. for node in IterNodeTree(d, rewindToRoot=True): print (node) # Will always yield the whole tree, no matter where we start. for node in IterateTree(d, True, True, True): print (node) """ def iter(node: c4d.GeListNode) -> typing.Iterator[c4d.GeListNode]: """Iterates over the descendants of the passed `node`. """ # The visited nodes and the node to stop iteration at. visited: dict[bytes, c4d.GeListNode] = {} terminal: c4d.GeListNode = node # Walking a tree iteratively is faster for medium to large trees than a recursive or # semi-recursive traversal. See: https://developers.maxon.net/forum/topic/13684 while node: # We could use GeListNode.__hash__ here, but it turns out that it is much slower than # doing it manually with MAXON_CREATOR_ID (sic!). Probably because of the overhead of # __hash__ hashing the UUID into an integer? nodeUuid: bytes = bytes(node.FindUniqueID(c4d.MAXON_CREATOR_ID)) if nodeUuid is None: raise RuntimeError(f"Could not retrieve UUID for {node}.") # Yield the node when it has not been encountered before. if visited.get(nodeUuid) is None: yield node visited[nodeUuid] = True # Walk the graph in a depth first fashion. getDown: c4d.GeListNode | None = node.GetDown() getNext: c4d.GeListNode | None = node.GetNext() getDownUuid: bytes | None = getDown.FindUniqueID(c4d.MAXON_CREATOR_ID) if getDown else None if getDown and getDownUuid is None: raise RuntimeError(f"Could not retrieve UUID for {getDown}.") if getDown and visited.get(bytes(getDownUuid)) is None: node = getDown elif node == terminal: break elif getNext: node = getNext else: node = node.GetUp() # --- End of iter() ---------------------------------------------------------------------------- if not isinstance(node, c4d.GeListNode): return # Set the starting node. # if rewindToRoot: # node = GetRootNode(node) # if rewindToFirstSibling: # node = GetFirstSiblingNode(node) # Traverse the hierarchy. while node: yield from iter(node) if not yieldSiblings: return node = node.GetNext()