• C4d Connector problem in 2024

    python
    6
    0 Votes
    6 Posts
    1k Views
    M
    Hi @filipst just to let you know that with the hotfix released yesterday(20/09/2023). The issue within Cinema 4D has been solved. In the same time make sure to use the latest version of the add-on in VsCode (v1.1.0). Cheers, Maxime.
  • Add normal button in a dialog that opens a dialog to select a folder

    python
    6
    0 Votes
    6 Posts
    1k Views
    ferdinandF
    Hey @danielsian, Yes, a tree view could be another solution, another one could be a GeUserArea. I deliberately did not mention both options since they are more complex. Especially TreeViewFunctions, the underlying interface for tree views, tends to be overwhelming for newcomers. I would recommend the dynamic UI workflow hinted at above, as this will result in the least amount of code. But it will of course not be as beautiful as something truly custom. Cheers, Ferdinand
  • MergeDocument under a selected Null

    python
    2
    0 Votes
    2 Posts
    604 Views
    ferdinandF
    Hey @pyr, Thank you for reaching out to us. The sentence 'if i do the whole thing via obj.InsertUnder(null) i miss the material' is a bit ambiguous, how do you mean that? I assume you mean that when you load two documents A and B and then start copying objects over by either cloning them or removing them from A and inserting them into B, that you then miss material references. Because the call you show us will load both objects and materials found at f into doc. A straightforward way to do what you want to do, parent everything that has been imported to a null, is to track the state of the top-level objects before and after the import. The difference between the states then tells you what to move where. In practice, there are some technical hoops to jump through, primarily the problem that you might run into dead references when you try to store objects references over the import process. Cheers, Ferdinand Result: moomoo.mp4 Code: """Demonstrates how to parent the objects merged into a document to a null object. Must be run as a script manager script and will import the contents of #FILE into the active document. """ import c4d doc: c4d.documents.BaseDocument # The active document. FILE: str = r"file:///e:/test.c4d" # The document to import. def main() -> None: """ """ # Get the UUID markers of all top level objects. We get the markers and not the object # references because importing a document bears a good chance that these references will be # dead (C4DAtom.IsAlive() == False) when we access them again. uuidCollection: list[bytes] = [] op: c4d.BaseObject = doc.GetFirstObject() while op: uuidCollection.append(bytes(op.FindUniqueID(c4d.MAXON_CREATOR_ID))) op = op.GetNext() # Now we import our document. c4d.documents.MergeDocument(doc, FILE, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS | c4d.SCENEFILTER_MERGESCENE) # We create our null object to parent the imported stuff to. We also add its UUID to # #uuidCollection, so that we do not attempt to parent #null to itself ;) null: c4d.BaseObject = c4d.BaseObject(c4d.Onull) null.SetName(FILE) doc.InsertObject(null) c4d.EventAdd() uuidCollection.append(bytes(null.FindUniqueID(c4d.MAXON_CREATOR_ID))) # Now we iterate again over all top level objects and parent everything that does not appear in # #uuidCollection to #null. op: c4d.BaseObject = doc.GetFirstObject() # We have to do this on two steps, because when we would already move #op in the first step # where we find the things to move, we would sabotage our own loop here (because as soon as # we move the first op to the null, op.GetNext() would be None). move: list[c4d.BaseObject] = [] while op: if bytes(op.FindUniqueID(c4d.MAXON_CREATOR_ID)) not in uuidCollection: move.append(op) op = op.GetNext() # Actually move the items. for op in move: op.InsertUnderLast(null) c4d.EventAdd() if __name__ == "__main__": main()
  • Casting GeListNode* to BaseObject*

    Moved
    10
    0 Votes
    10 Posts
    2k Views
    ferdinandF
    Hey @spedler, I would recommend having a look at the migration guide and the example.migration_2024 in the SDK. When you are then stuck, I would invite you to either open a new thread with concrete code higghlighting your problem or reaching out to us confidentially via our contact form and share there data with us, in case you are not at liberty of sharing your code publicly. We are here to help you along some more bumpy passages of migrating your plugin. Cheers, Ferdinand
  • Can you get a list of missing plugins?

    python
    3
    1
    0 Votes
    3 Posts
    1k Views
    K
    Thanks for the fast response @ferdinand . Sorry that the title of my question is somewhat misleading. The thread that you've linked is actually quite detailed and useful I'll take a closer look into that.
  • How to find and delete a node in a NodeGraph?

    c++ 2023
    5
    0 Votes
    5 Posts
    2k Views
    kbarK
    @ferdinand said in How to find and delete a node in a NodeGraph?: Fig. I: The two IDs are displayed in the node editor info panel for the selected node. Make sure to have Preferences\Node Editor\Ids enabled to also see the node ID in addition to the asset ID. I didn't know about this! I have been copying and pasting the selected nodes into a text editor up until now, which in itself is a very cool feature.
  • C4D 2023 on macOS: GeUserArea::DrawBitmap() not working anymore?

    2023 c++ macos
    8
    0 Votes
    8 Posts
    2k Views
    fwilleke80F
    I already tried using 8 bit greyscale. Still doesn't work in 2023 on macOS, but on the other C4D/OS combinations. Will try RGB, too. If that doesn't work either, I'll be back and try to write a minimal example. Cheers, Frank
  • Gradient Interpolation Error in 2024 release

    python 2024
    3
    0 Votes
    3 Posts
    665 Views
    D
    @m_adam Brilliant! Thanks a lot!
  • Running Automatic UV for all objects

    python windows
    3
    1
    1 Votes
    3 Posts
    2k Views
    M
    Hi @BineeMan sorry for the late reply I was pretty busy Friday and thanks @Dunhou for demonstrating how Packed Auto UV work. For Cubic and Angle you need to use c4d.modules.bodypaint.CallUVComman as demonstrated in the call_uv_command_ example. So for the Angle automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALMAPPING with the next parameters: c4d.OPTIMALMAPPING_PRESERVEORIENTATION # Preserve Orientation c4d.OPTIMALMAPPING_STRETCHTOFIT # Stretch to Fit c4d.OPTIMALMAPPING_DISTORTION # Maximu, Distortion c4d.OPTIMALMAPPING_RELAXCOUNT # Relaxation Steps c4d.OPTIMALMAPPING_SPACING # Spacing And for the Cubic automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALCUBICMAPPING with the next parameters: c4d.OPTIMALMAPPING_PRESERVEORIENTATION # Preserve Orientation c4d.OPTIMALMAPPING_STRETCHTOFIT # Stretch to Fit c4d.OPTIMALMAPPING_TWOD # 2D c4d.OPTIMALMAPPING_AREAFAK # Maximum Area Factor c4d.OPTIMALMAPPING_RELAXCOUNT # Relaxation Steps c4d.OPTIMALMAPPING_SPACING # Spacing Cheers, Maxime.
  • 2024 Conversion questions

    2024 c++
    2
    0 Votes
    2 Posts
    604 Views
    ferdinandF
    Hey @Rox, Thank you for reaching out to us. You really should have a look at the migration guide, as these topics are covered there. We cannot debug every compiler error here for you, nevertheless here are the answers to your three problems: // 1 - That should fix it as the signature is now: // GeData( const CustomDataType & data, Int32 type ) t_data = GeData(bbs, CUSTOMDATATYPE_BITMAPBUTTON); // 2 - Selections are now COW and make a distinction between being mutable and immutable. const BaseSelect* const mySelection = sTag->GetBaseSelect(); // immutable BaseSelect* const mySelection = sTag->GetWritableBaseSelect(); // mutable // 3 - Same story here, GetCustomDataType returns immutable data now, the method is also templated // now, so you do not need to cast things anymore. const InExcludeData* const myList= bc->GetCustomDataType<InExcludeData>(MY_ID); InExcludeData* const myList= bc->GetCustomDataTypeWritableObsolete<InExcludeData>(MY_ID); Cheers, Ferdinand edit: forgot the template argument in BaseContainer::GetCustomDataType
  • Dynamically changing custom icon of TagData Plugin

    python s26 windows
    6
    2
    1 Votes
    6 Posts
    1k Views
    ferdinandF
    Hello @HerrMay, Thank you for the updated information. I had a look at your problem, and I could reproduce it, but it is primarily a problem of your code as suspected. I document below how I analyzed this problem not as a passive aggressive expression of "look how easy that is!". But to give an insight on how to approach such debugging task. The core rule is here to question one's own assumptions and assure that things are indeed how we think they are. Debugging So, when one follows your steps, one indeed ends up with something like this: [image: 1694702115621-8fdef1aa-1a5e-43d8-8cdb-42b480fd4f0d-image.png] Your assumption here was that the icon handling is bugged on our side. For that to be true, the tag on Null.2 would have to be IDC_ACTIVE_ON but with the icon handling failing and Cinemas 4D rendering it as disabled. So, I checked the value with this code: # Set custom icon for node. if id == c4d.MSG_GETCUSTOMICON: host: c4d.BaseObject = tag.GetObject() isFirst: bool = host.GetUp() == None and host.GetPred() == None if isFirst: print(f"TOP: {host.GetName() = }, {tag[IDC_ACTIVE] = }") else: print(f"OTHER: {host.GetName() = }, {tag[IDC_ACTIVE] = }") icon = self.PLUGIN_ICON_INACTIVE if tag[IDC_ACTIVE] == IDC_ACTIVE_OFF else self.PLUGIN_ICON_ACTIVE data["bmp"] = icon data["w"] = icon.GetBw() data["h"] = icon.GetBh() data["filled"] = True Which will print this when you add the tags: [image: 1694702486106-baebed6a-fdc0-4d53-8214-1d6b2011cad8-image.png] So, the topmost tag is indeed inactive and the icon rendering is correct. The question is now: 'Why is that tag inactive because we initialize all tags as node[IDC_ACTIVE] = IDC_ACTIVE_ON ?' The likely culprit is just a few lines above, as we toggle there the active state of our tag. # Handle double click on tag. if id == c4d.MSG_EDIT: if tag[IDC_ACTIVE] == IDC_ACTIVE_OFF: tag[IDC_ACTIVE] = IDC_ACTIVE_ON else: tag[IDC_ACTIVE] = IDC_ACTIVE_OFF And indeed, when we comment out these lines, we end up with this when creating multiple new tags. [image: 1694702820259-ebddd8e2-ff78-47f3-a4ca-40cbd26ac047-image.png] Moreover, when we revert our change of commenting out that code and test our assumption 'only happens for multi-object selections', we can see that this does not hold true: [image: 1694703033146-e5f2a8dd-2ebd-4555-8188-d769b84895e3-image.png] We can now check our own comment: # Handle double click on tag. if id == c4d.MSG_EDIT: ... and check MSG_EDIT: [image: 1694703119443-de39ee11-81d9-4e68-a2a6-a4c4dd182b84-image.png] The documentation says, "for example". So, the event does not capture a double click, but double clicking a BaseObject to edit its name is one example of an edit event. Adding a tag to an object apparently also emits MSG_EDIT to the tag, and when you have a multi-selection, MSG_EDIT is only emitted to the first element in the selection. Conclusion That behavior of MSG_EDIT in regard to tags and multi-selections is of course quite odd, but I would not consider it to be a bug, as an edit event is not really defined for tags in the first place. And while I understand what you want to do here, I would also say that what you do in MSG_EDIT is a violation of the UX principles of Cinema 4D. Double clicking a tag should not edit a random parameter in it. We strive for Cinema 4D having a seamless UX-experience for users including plugins. So, we rather not support it when plugin authors are trying to break these UX rules. But I will nevertheless talk with my colleagues about the subject and if we want to consider this behavior of MSG_EDIT to be a bug. Cheers, Ferdinand
  • Can't snap to Generator output spline

    r25 python windows
    4
    0 Votes
    4 Posts
    909 Views
    ferdinandF
    Hey @Madara, in short, a Python generator object is not meant to generate splines. You can collapse the cache of your constructed spline and return its LineObject cache instead of the spline itself. For an example, see [1]. In your case that would be connect_obj. This will work in the sense that the generator cache will be then like a SplineObject cache and when you 'Current State to Object' your generator, it will behave like a spline and give you the interpolated editable linear spline and not a spline generator. However, spline snapping not only inspects caches, but also checks if OBJECT_ISSPLINE is true. So, you cannot bamboozle it by faking a SplineObject cache. You will have to implement a proper spline plugin for what you want to do, as you can overwrite there GetContour Cheers, Ferdinand PS: Your code contains multiple elements that can crash Cinema 4D and/or corrupt the loaded document. Specifically, these sections: if link_clone: #Parametric object pobj = u.SendModelingCommand( command = c4d.MCOMMAND_CURRENTSTATETOOBJECT, list = [connect_obj], mode = c4d.MODELINGCOMMANDMODE_ALL, doc = op.GetMain()) connect_obj = pobj[0] ... offspline = u.SendModelingCommand( c4d.MCOMMAND_SPLINE_CREATEOUTLINE, [connect_obj], c4d.MODELINGCOMMANDMODE_ALL, bc=settings, doc=doc) A Python generator's main() function is subject to the threading restrictions of Cinema 4D because it is just a wrapper for ObjectData.GetVirtualObjects. Executing SendModelingCommand in a threaded context is fine, but you cannot do it on the active document (both doc and op.GetDocument() are the active document). This is here even worsened by the fact that that the objects in list are not actually part of the document you pass as doc. To circumvent that you must create your own document just as I have in [1] with temp. Find a more detailed example of that technique in smc_extrude_s26.py. [1] import c4d op: c4d.BaseObject # The Python generator object. def main() -> c4d.BaseObject: """ """ # A user data Boolean to toggle collapsing the cache of this generator. collapse: bool = op[c4d.ID_USERDATA, 1] spline: c4d.BaseObject = c4d.BaseObject(c4d.Osplinecircle) if not spline: return c4d.BaseObject(c4d.Onull) # Just return the circle spline itself when #collapse is false. if not collapse: return spline # Otherwise grab the LineObject cache of the spline and return a copy of it. temp: c4d.documents.BaseDocument = c4d.documents.BaseDocument() temp.InsertObject(spline) temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, c4d.BUILDFLAGS_NONE) cache: c4d.LineObject | None = spline.GetCache() if not isinstance(cache, c4d.LineObject): return c4d.BaseObject(c4d.Onull) return cache.GetClone(c4d.COPYFLAGS_NONE)
  • GetDefaultValue Not Working in C4D 2024

    python
    3
    0 Votes
    3 Posts
    556 Views
    E
    @m_adam I'm looking forward to the next version update. Thank you for your response.
  • Converting ABC file to AEC using c4dpy?

    windows python
    3
    0 Votes
    3 Posts
    484 Views
    S
    Hi Maxime, thanks for the information - so just to clarify, I need to use RDATA_PROJECTFILESAVE in order to export as an AEC?
  • Script path instead of call command in Python

    2023 python
    2
    0 Votes
    2 Posts
    603 Views
    ferdinandF
    Hello @specnazspe, Welcome to the Plugin Café forum and the Cinema 4D development 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 Guidelines, as they line out details about the Maxon SDK Group support procedures. Of special importance are: Support Procedures: Scope of Support: Lines out the things we will do and what we will not do. Support Procedures: Confidential Data: Most questions should be accompanied by code but code cannot always be shared publicly. This section explains how to share code confidentially with Maxon. Forum Structure and Features: Lines out how the forum works. Structure of a Question: Lines out how to ask a good technical question. It is not mandatory to follow this exactly, but you should follow the idea of keeping things short and mentioning your primary question in a clear manner. About your First Question Command IDs are assigned dynamically to a script and therefore can change. It seems also a bit overly complicated to have a tag that evaluates user data interactions on its host to call a command wrapping a script. Commands are based on IDs, so you cannot pass them a script path, but Python itself has one million and one way to have Python code execute other Python code. Since you want to use the content of your script just as a library, you should make it one. Place the logic of it in a library apth of your liking and import that logic both into your Script manager script and Python tag when needed. You could use the std lib function runpy to run the code found at a script path yourself. Basically, what I did in run_script here. Find here the matching thread. You could also just manipulate sys.path from your tag to inject the path where your script lies, so you can then import the script like a module. I would not recommend doing point 2 or 3 because they are both error prone. Just expose the commonly used functionality used both by the script and tag as a lib. edit: As pointed out by @m_adam, since S26, you can also evaluate the dynamic ID of scripting objects from Python. Finde an example in load_python_script_r26.py. The bread and butter of this is c4d.GetDynamicScriptID. Cheers, Ferdinand
  • 0 Votes
    5 Posts
    865 Views
    F
    @m_adam OK. Thank you very much. Cheers
  • Nesting Pivot object causes parent to shoot up into space

    6
    0 Votes
    6 Posts
    979 Views
    ferdinandF
    Hey @HerrMay, Great to hear that you solved your problem. Cheers, Ferdinand
  • How can I use Plane Cut in Python

    python 2023 windows
    3
    0 Votes
    3 Posts
    667 Views
    C
    Thank you very much! Now it is working.
  • urllib.request module is missing

    5
    0 Votes
    5 Posts
    1k Views
    B
    @ferdinand Brilliant, thank you.
  • Can I use both MouseDown() and DoubleClick() in treeview?

    python windows 2023
    3
    0 Votes
    3 Posts
    658 Views
    DunhouD
    Hi @ferdinand , @ferdinand said in Can I use both MouseDown() and DoubleClick() in treeview?: In general, you should keep in mind that the concept of a double click is only represented very weakly in Cinema 4D, with TreeViewFunctions being one of the very few cases which uses that term. In the backbone of the API, the GUI message stream, the concept of a double click does not exist. Things are actions (BFM_ACTION_...) and inputs (BFM_INPUT_...) and each mouse click counts as an input and/or action. It is up to specific gadgets to bundle up multiple clicks into a double click. That helps foe better unstand how it worked, thanks! And this kind solution is a much more mature solution than what I used a simple one in my Octane Light Manager, I try to stop the set check event duplicate many times, I will keep more condition as your suggestion. [image: 1693920299018-e0be12a8-f277-400d-9d7a-14065e55ffee-image.png] Thanks again for your detailed explain for all the answer. Cheers~ DunHou