• 0 Votes
    3 Posts
    771 Views
    O
    @ferdinand Thank you for the detailed explanation and the solution! This perfectly solved my problem, and I really appreciate you clarifying how to access the DESC_CYCLE values!!
  • 0 Votes
    4 Posts
    1k Views
    ferdinandF
    We will very likely never abandon this technique. The issue is more that there are a lot of hacks in the Spline Mask code and almost none of them are shown in the public code example. And we cannot publish all of these hacks both for strategic (we do not want to show all of our internals) and practical reasons (we cannot explain all the nitty gritty details there). It was not such a good decision to publish that code example; which as far as I understood came to pass as a user asked as to how the Spline Mask object has been realized. But we probably should just have said "no" then. Just as much as I probably should not have shown you that simulation hack. Hacks lead to more hacks and more problems. Cheers, Ferdinand PS: And to be super verbose, you ONLY need this pattern when you "have to" implement a spline that has other (spline) objects as inputs. When you have implemented regular splines like this, you should probably revert that.
  • 0 Votes
    7 Posts
    2k Views
    ferdinandF
    Good to hear!
  • 0 Votes
    4 Posts
    854 Views
    1
    Thank you very much for your answer! I already know how to do it, and your answer has given me some ideas.
  • 0 Votes
    13 Posts
    3k Views
    K
    Hi @ferdinand , We are starting to run here in circles. Please consider applying for MRD as lined out here and via chat. I think that makes sense. I'll apply for MRD right away.
  • 0 Votes
    2 Posts
    510 Views
    ferdinandF
    Hello @phillipg, 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 I am not quite sure where you came across the information that it would be impossible to do this, but you can allocate and add a sound track just as any other special track as documented. Please post your code for future support requests. Cheers, Ferdinand """Adds a sound track to the selected object, unless it already has one. """ 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: return c4d.gui.MessageDialog("Please select an object.") # The description ID for a sound track and check if it already exists on the object. did: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.CTsound, c4d.CTsound, 0)) if op.FindCTrack(did): return c4d.gui.MessageDialog("The object already has a sound track.") # Create a new sound track and add it to the object. track: c4d.CTrack = c4d.CTrack(op, did) op.InsertTrackSorted(track) c4d.EventAdd() if __name__ == '__main__': main()
  • 0 Votes
    3 Posts
    899 Views
    K
    Thansk so much for the video, that was a deep dive! For my issue, I resolved it like this: def message(id, data): if(id==c4d.MSG_DESCRIPTION_POSTSETPARAMETER): userDataID = eval(str(data['descid']))[1][0] if userDataID in [2, 4, 5]: c4d.CallButton(op, c4d.OPYTHON_MAKEDIRTY) so, if any user data touched (including fieldlist), the generator updates itself.
  • Load presets args in python?

    Moved Bugs windows python 2025
    7
    1
    0 Votes
    7 Posts
    2k Views
    DunhouD
    It sounds worth a try, but there may be latency issues when it comes to changes, or data changes can be manually processed during the changes Perhaps for my needs, I can force the database to be use GetSceneRepository, anyway, it's worth a try. Thank you for your guidance
  • 5 Votes
    10 Posts
    2k Views
    ferdinandF
    Hey @Dunhou, I am still not 100% clear about what you are trying to do. But I guess what you want to do is distinguish a single-drag -click, i.e., the user is dragging something, from a single click. The issue with that is that we are in your code inside a while loop which just polls the input state as fast as it can and not in message stream, where we only get events for state changes. So, this means unless there is Speedy Gonzales at the mouse, even the quickest of single clicks will produce more than one iteration in the loop. What is still unclear to me why you are doing all this, as knowing that the mouse is outside of the UA does not mean that we know if the user dropped the payload on an object. But this is how I would solve distinguishing a 'light click' (a single click) from a drag event. A cleaner solution might be to let the convenance function InputEvent be a convenance function and move to the source Message. There you should be issue start and stop events for drag operations. But since you want to start it yourself, we are sort of in a pickle. I would have to play around a bit with the code to see if there is a better way with Message, Cheers, Ferdinand def InputEvent(self, msg: c4d.BaseContainer) -> bool: """Called by Cinema 4D when the user area receives input events. Here we implement creating drag events when the user drags from this user area. The type of drag event which is initiated is determined by the drag type selected in the combo box of the dialog. """ # When this is not a left mouse button event on this user area, we just get out without # consuming the event (by returning False). if msg[c4d.BFM_INPUT_DEVICE] != c4d.BFM_INPUT_MOUSE and msg[c4d.BFM_INPUT_CHANNEL] != c4d.BFM_INPUT_MOUSELEFT: return False dragType: int = self._host.GetInt32(self._host.ID_DRAG_TYPE) mx = int(msg[c4d.BFM_INPUT_X]) my = int(msg[c4d.BFM_INPUT_Y]) mx -= self.Local2Global()["x"] my -= self.Local2Global()["y"] state = c4d.BaseContainer() self.MouseDragStart(c4d.BFM_INPUT_MOUSELEFT,mx,my,c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE|c4d.MOUSEDRAGFLAGS_NOMOVE) lastPos: tuple[float, float] | None = None while True: res, dx, dy, channels = self.MouseDrag() if res != c4d.MOUSEDRAGRESULT_CONTINUE: break self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, state) # This is how I debugged this, GetContainerTreeString (in the beta it might be already # contained) is a feature of a future version of the SDK. # print(f"{mxutils.GetContainerTreeString(state, 'BFM_')}") # State: Root (None , id = -1): # ├── BFM_INPUT_QUALIFIER (DTYPE_LONG): 0 # ├── BFM_INPUT_MODIFIERS (DTYPE_LONG): 0 # ├── BFM_INPUT_DEVICE (DTYPE_LONG): 1836021107 # ├── BFM_INPUT_CHANNEL (DTYPE_LONG): 1 # ├── BFM_INPUT_VALUE (DTYPE_LONG): 1 # ├── BFM_INPUT_VALUE_REAL (DTYPE_REAL): 0.0001 # ├── BFM_INPUT_X (DTYPE_REAL): 203.13671875 # ├── BFM_INPUT_Y (DTYPE_REAL): 88.0390625 # ├── BFM_INPUT_Z (DTYPE_REAL): 0.0 # ├── BFM_INPUT_ORIENTATION (DTYPE_REAL): 0.0 # ├── 1768977011 (DTYPE_REAL): 1.0 # ├── BFM_INPUT_TILT (DTYPE_REAL): 0.0 # ├── BFM_INPUT_FINGERWHEEL (DTYPE_REAL): 0.0 # ├── BFM_INPUT_P_ROTATION (DTYPE_REAL): 0.0 # └── BFM_INPUT_DOUBLECLICK (DTYPE_LONG): 0 # I.e., we are unfortunately neither being issued a BFM_DRAGSTART nor an # c4d.BFM_INTERACTSTART, I assume both or only emitted in the direct Message() loop. # But we can write code like this. # if state[c4d.BFM_INPUT_DOUBLECLICK]: # print(f"Double click detected at {mx}, {my}") # break # elif state[c4d.BFM_INPUT_VALUE] != 1: # print(f"Mouse button not pressed anymore at {mx}, {my}") # break # else: # print(f"Non double click at {mx}, {my}") # The issue with this is that we are here just in a loop polling the current left button # state, not inside a message function where we get a state stream. So, for a single # click, we end up with somewhat like this, and here I made sure to click really fast # Non double click at 96.8515625, 58.37109375 # Non double click at 96.8515625, 58.37109375 # Non double click at 96.8515625, 58.37109375 # Non double click at 96.8515625, 58.37109375 # Mouse button not pressed anymore at 96.8515625, 58.37109375 # And this is a short drag event. # Non double click at 84.875, 56.5859375 # Non double click at 84.875, 56.5859375 # Non double click at 84.875, 56.5859375 # Non double click at 84.875, 56.5859375 # Non double click at 84.59765625, 56.5859375 # Non double click at 83.49609375, 56.94921875 # Non double click at 83.49609375, 56.94921875 # Non double click at 82.39453125, 57.3125 # Non double click at 82.39453125, 57.3125 # Non double click at 80.74609375, 58.1328125 # Non double click at 80.74609375, 58.1328125 # Non double click at 77.7265625, 58.6328125 # ... # Non double click at -8.35546875, 80.16796875 # Non double click at -8.35546875, 80.16796875 # Non double click at -8.35546875, 80.16796875 # Mouse button not pressed anymore at -8.35546875, 80.16796875 # So they are very similar, and we cannot go by the pure logic "when the coordinates # do not change, we are in a drag event" because this is not an event stream, i.e., we # might poll the same input state multiple times, depending on how fast our #while loop # runs. # But what we could do, is postpone all actions until we see a change. In extreme cases, # where the user is swiping very fast with the mouse and then clicks on a tile, this might # fail. mx -= dx my -= dy currentPos: tuple[float, float] = (mx, my) if lastPos is None and currentPos != lastPos: lastPos = currentPos # The mouse is not being pressed anymore. if not state[c4d.BFM_INPUT_VALUE]: if currentPos != lastPos: print("Drag event") else: print("Click event") break return True Click event Drag event Click event Click event Click event Drag event
  • 0 Votes
    4 Posts
    923 Views
    ferdinandF
    Hey @lionlion44, Well, doing what you want to do is only halfway possible. An object being soloed just means setting the flag EHIDE on all othe objects. And while there is OHIDE which can be used to hide scene elements in managers, e.g., an object in the Object Manager, it is not being used by the 'Set as Root/Path Bar'-function of the Object Manager. So, you cannot hook into that. What you can do is just write a simple script which operates both flags for you. But to make this air-tight, you will have to implement a plugin (to avoid accidentally saving a scene with hidden elements). Cheers, Ferdinand Result vid.mp4 Code """Demonstrates how to set the visibility of objects in the viewport and the managers. - When CTRL is not pressed while the script is invoked, the visibility state of the selected objects is set, i.e., everything that is not selected will be hidden. - When CTRL is pressed, the visibility state of the selected objects is cleared. - When HIDE_DESCENDANTS_OF_SELECTED_OBJECTS is set to True, the descendants of selected objects will be considered selected as well. WARNING: The visibility state is written PERMANENTLY into the scene graph, i.e., when one hides objects and saves the scene, the visibility state will be saved as well. One can of course just run the script while pressing CTRL to clear the visibility state of such saved and then loaded back scene, but this could possibly brick scenes for other users. To make this air-tight, one would have to implement a plugin which handles un-hiding objects before a scene is being saved (which is not super trivial in Python atm). Use this script at your own risk. """ __author__ = "Ferdinand Hoppe" __copyright__ = "Copyright 2025, Maxon Computer GmbH" import c4d import mxutils doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. # Wether to hide objects that are descendants of selected objects. I.e., when you have A-> B -> C, # and A is selected, B and C will be considered selected as well. HIDE_DESCENDANTS_OF_SELECTED_OBJECTS: bool = True def IsSelected(node: c4d.BaseObject) -> bool: """Returns if #node is selected or if any of its predecessors are selected (when selecting descendants implicitly is enabled). """ while node: if node.GetBit(c4d.BIT_ACTIVE): return True if not HIDE_DESCENDANTS_OF_SELECTED_OBJECTS: break node = node.GetUp() return False def SetObjectVisibility(doc: c4d.documents.BaseDocument, clearSate: bool = False) -> None: """Sets the visibility of the object in the managers and the viewport. """ for node in mxutils.IterateTree(doc.GetFirstObject(), True, True, True): isSelected: bool = clearSate or IsSelected(node) node.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_CLEAR if isSelected else c4d.NBITCONTROL_SET) node.ChangeNBit(c4d.NBIT_EHIDE, c4d.NBITCONTROL_CLEAR if isSelected else c4d.NBITCONTROL_SET) def main() -> None: """Called by Cinema 4D whhen the """ if not op: c4d.gui.MessageDialog("No object selected.") return state: c4d.BaseContainer = c4d.BaseContainer() if not c4d.gui.GetInputState(c4d.BFM_INPUT_MOUSE, 0, state): raise RuntimeError("Failed to get input state") ctrlIsPressed: bool = state[c4d.BFM_INPUT_QUALIFIER] & c4d.QUALIFIER_CTRL # If Ctrl is pressed, clear the visibility state, otherwise set it. SetObjectVisibility(doc, True if ctrlIsPressed else False) c4d.EventAdd() if __name__ == '__main__': main()
  • plugin Loupedeck CT with Redshift

    Cinema 4D SDK python 2025 windows
    2
    2
    0 Votes
    2 Posts
    636 Views
    M
    Hi @shafy sorry for the delay I was away for few times. Before we start please read the Support Procedure - About AI-Supported Development. Keep in mind we are not there to develop for you neither integrate or guide you with 3rd party module that you will most likely need in order to control knobs. With that's said what you are asking (at least retrieving a parameter and defining it) is possible for Redshift light intensity. It is as easy as drag and dropping the parameter you are interested by into the Cinema 4D Python Console as explained in Python Script Manager Manual - Drag And Drop. Additionally you can also use the Script Log to record a script or change and therefor re-execute it. Cheers, Maxime.
  • 0 Votes
    4 Posts
    974 Views
    uogygiuolU
    This is fantastic, thank you so much! This works beautifully. I see your confusion with the "offset". This is absolutely my fault in formulating the question. Sorry about that. I also wanted the clones to follow a spline with an individual offset, but then thought to remove this part to not overcomplicate the question and accidentally left some traces. However, in your video you gave me valuable insight in how to achieve this as well. I already have a working sketch where I'm daisy chaining your script with a Spline Effector in the right order. Giving these thoughtful answers is incredibly valuable, you can't believe how motivating this is to go further.
  • 0 Votes
    4 Posts
    1k Views
    ferdinandF
    Hey @Fabio-B, Please note that we cannot provide support for AI generated code. See Support Procedures: Scope of Support for details. When you want the clones to move, or in other words to be animated over time, you will need a noise and cannot use hashes or random values, as they are not interpolated, i.e., continous. """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 import mxutils 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 # Get the matrices of the particles. This is the array we will modify. matrices: list[c4d.Matrix] = data.GetArray(c4d.MODATA_MATRIX) # For each particle write a new persistent random height value, hashed over the index of the # particle. This will cause the height each time to be the same, as long as the index of the # particle does not change. One could also hash the position of the original particle to get a # height value that is not dependent on the index of the particle (e.g., when the user changes # the order of the particles in the Matrix object). for i in range(data.GetCount()): pos: c4d.Vector = matrices[i].off y: float = c4d.utils.noise.Noise(matrices[i].off, doc.GetTime().Get()) * 25.0 matrices[i].off = c4d.Vector(pos.x, y, pos.z) # Hash a height value over the position of the particle. # y: float = mxutils.g_random.HashVectorToNumber(matrices[i].off) * 25.0 # Write the new data back. data.SetArray(c4d.MODATA_MATRIX, matrices, op[c4d.FIELDS].HasContent()) return True Recording 2025-05-02 141611.mp4 Cheers, Ferdinand
  • lhit from BaseVolumeData

    Cinema 4D SDK r21 python windows
    3
    0 Votes
    3 Posts
    748 Views
    JH23J
    Hey @m_adam, Thanks for the reply! What you’re saying makes a lot of sense C++ is clearly the better option for this kind of stuff, especially when you need more control and better performance. I haven’t had much chance to dive into C++ yet, but I’m well aware it’s way more powerful than Python when it comes to working with the engine. It’s a bit of a shame that there’s no way to directly access polygon info from Python, since that’s exactly what I needed. But still, I really appreciate you taking the time to explain it. Cheers, James H.
  • Maxon API for Python

    Cinema 4D SDK python 2025 windows
    3
    0 Votes
    3 Posts
    661 Views
    ThomasBT
    @ferdinand Thank you for your detailed explanation, it shed some light on the whole matter for me.
  • Frozen Matrix different

    Cinema 4D SDK 2025 python
    4
    1
    0 Votes
    4 Posts
    1k Views
    kangddanK
    @i_mazlov said in Frozen Matrix different: 象,并且没有其 I found that it's very similar to the offsetParentMatrix attribute introduced in Maya 2020.:)
  • C4D Python: Redshift IPR Start/Stop Control

    Cinema 4D SDK python windows 2025
    5
    0 Votes
    5 Posts
    1k Views
    M
    @hoganXYZ hi if it does not appear in the script log then there is no clear rules. Sometimes there is another way sometimes now it depends on how it is implemented. For the one you asked I do not think it is possible. Best bet would be to ask Redshift on their forum. Cheers, Maxime.
  • Set RenderData framerate causing C4D to crash

    Moved Bugs python 2025
    4
    0 Votes
    4 Posts
    22k Views
    M
    Hi I was able to reproduce the issue and fix it for the next version. The bug is that setting RDATA_FRAMERATE require the RenderData to be part of a document in case the "Use Project Frame Rate" is enabled on the render settings. If this is enabled and this is enabled by default the value you enter will be disregarded. "Use Project Frame Rate" is a new setting added in 2025.2. I will ping you once the fix is available. Cheers, Maxime.
  • 0 Votes
    3 Posts
    635 Views
    I
    Hi Ferdinand, Thanks a lot for the clear explanation and for taking the time to share the code. The part about accessing BaseVideoPost and using REDSHIFT_RENDERER_EMISSION_ENABLE was spot on. Exactly what I needed. Works perfectly! Really appreciate it. Anthony