• EnhanceMainMenu Inconsistent results in different c4d versions

    2023 python
    3
    1
    0 Votes
    3 Posts
    467 Views
    chuanzhenC
    @i_mazlov Thanks
  • 0 Votes
    3 Posts
    697 Views
    B
    @i_mazlov Gotcha. Thanks. Works as expected.
  • Inserting items into dropbox to TreeView

    4
    1
    0 Votes
    4 Posts
    784 Views
    M
    Hi @simonator420 for this purpose you have to define GetDropDownMenu and SetDropDownMenu. GetDropDownMenu is called by Cinema 4D to retrieve the content of the menu to display from the menuInfo dictionary. For more information about this dictionary please have a look at TreeViewDropDownMenuInfo Dict. SetDropDownMenu is called by Cinema 4D when the user select a value from the dropdown. Find bellow an implementation example: def GetDropDownMenu(self, root, userdata, obj: Entry, lColumn, menuInfo): menuInfo["entry"] = 1001 # Select teh second entry menuInfo["menu"][1000] = "First Entry" menuInfo["menu"][1001] = "Second Entry" menuInfo["menu"][1002] = "ThirdEntry" menuInfo["state"] = int(menuInfo["state"]) # Workaround to not have warning in Cinema 4D. Will be fixed in the next C4D version. def SetDropDownMenu(self, root, userdata, obj, lColumn, entry): print(f"User selected the entry with the ID: {entry}") Finally note that due to an issue that will be fixed in the upcoming version of Cinema 4D, even if you do not change the state, you need to override it to an int value otherwise it will print an error in the console (this is not blocking but still). Cheers, Maxime.
  • MoGraph color tag access

    c++
    4
    0 Votes
    4 Posts
    952 Views
    M
    Hi @Aaron sorry I have to admit I totally overlooked your case. So Fracture Object is a bit special in how color is handled, and this is not as an usual mograph object, where there is Modata and an array of color. Instead each part is a different PolygonObject and the color is simply the color of the object (the one from the basic tab when you select a polygon object in the attribute manager). So with that said find bellow a code to read color. // Get the fracture object BaseObject* obj = doc->GetFirstObject(); if (!obj && obj->GetType() == 1036557) return false; // Retrieve the cache. The Cache of a fracture object consist of a null and a bunch of PolygonObject where // each PolygonObject represents a part of the fracture. BaseObject* rootObjCache = obj->GetCache(); if (!rootObjCache) return false; BaseObject* child = rootObjCache->GetDown(); Random randomGenerator = Random(); while (child) { // Read Color GeData data; child->GetParameter(DescID(ID_BASEOBJECT_COLOR), data, DESCFLAGS_GET::NONE); Vector color = data.GetVector(); ApplicationOutput("@"_s, color); // Write Color with new random value, note that we write the cache, meaning each new re-evaluation of the Fracture object will reset these changes Vector randomColor = Vector(randomGenerator.Get01(), randomGenerator.Get01(), randomGenerator.Get01()); child->SetParameter(DescLevel(ID_BASEOBJECT_COLOR), randomColor, DESCFLAGS_SET::NONE); // Get Next Polygon Object aka the next part from the fracture child = child->GetNext(); } Cheers, Maxime.
  • Rendering render queue on program startup

    Moved
    6
    0 Votes
    6 Posts
    941 Views
    Z
    Hi @karol_w! This is exactly what I am looking for, because Octane keeps crashing on me. Unfortunately, I am not very good with coding. Is there any chance you would share your plug-in? Or point me towards where to find resources to build that myself? Thank you very much!
  • GetActiveUVSet() returns None if multiple UVs are active?

    python
    4
    0 Votes
    4 Posts
    950 Views
    ferdinandF
    Hello @Gene, So far it's doing exactly what I need. Only issue I have is if I need to undo the script. I have to press the Undo button several times because C4D creates an undo state for every Tag/Object selection step in the script, instead of putting all of them under one undo state. I think this is a long-existing limitation of C4D's Undo system? Well, it depends a bit on what you would consider a "long-existing limitation", but you can consolidate multiple operations into a singular item in the undo-stack. The most relevant methods are BaseDocument.StartUndo, BaseDocument.EndUndo, and BaseDocument.AddUndo (there more undo related methods on BaseDocument). Cheers, Ferdinand """Demonstrates wrapping selecting all top-level objects in a document into one undo item. """ import c4d doc: c4d.documents.BaseDocument def main(): """ """ obj: c4d.BaseObject = doc.GetFirstObject() # Start an undo item in the undo stack. doc.StartUndo() while obj: # Add an operation to the undo item, most operations must be invoked BEFORE the actual # operation is carried out. The only exception is inserting new nodes, AddUndo must here # be called AFTER the operation. doc.AddUndo(c4d.UNDOTYPE_ACTIVATE, obj) doc.SetActiveObject( obj, c4d.SELECTION_NEW if obj == doc.GetFirstObject() else c4d.SELECTION_ADD) obj = obj.GetNext() # Close the item. doc.EndUndo() c4d.EventAdd() if __name__ == "__main__": main()
  • FieldLayer Plugin Globals

    r20 sdk c++
    3
    0 Votes
    3 Posts
    813 Views
    J
    Thanks for the response, that seems to have solved the problem.
  • Multi-menu menu, how to

    sdk c++
    5
    1
    0 Votes
    5 Posts
    1k Views
    WickedPW
    Thanks @ferdinand, Agree. I've avoided using OS-specific calls up until now. But for this one I've had to make an exception. Not an ideal solution, but it seems to work: [image: 1681381734409-e9057cac-086c-4810-bb9c-c198ecdc2427-image.png] To get around the lost window focus issue, I'm using the first dialog's Timer() to poll and check if any dialog in the menu has window focus. If none do, e.g., a user has clicked away, the system self-destructs. If a menu selection is made, it sends a Message() back to the calling object with details, and then self-destructs. It's all a bit of a hack. But it works. WP. Update: I'll mark this thread as solved. If I have any further questions I'll pop back in.
  • Cinema 4D Unable to use MATLAB library

    s26 c++
    4
    0 Votes
    4 Posts
    713 Views
    i_mazlovI
    Hello @pchg, Please note that the subject is out of scope of support as declared in the forum guidelines, namely: We cannot provide support for third party libraries. As a direction of your further investigation you can consider checking MATLAB library for using incompatible dependencies. Another guess would be to check if plugin registration succeeds at all. Thanks for your understanding! Cheers, Ilia
  • Cinema 4D Connector not working on 2023.2.0 on Air M1 MacOs 12.6.3

    c++ 2023
    3
    0 Votes
    3 Posts
    904 Views
    mikeudinM
    Thank you @ferdinand !
  • 0 Votes
    9 Posts
    2k Views
    A
    Hi @Manuel I have checked the new update 2023.2.0 and the feature works just fine with custom bitmaps without any change from my side. Congrats with a very quick bugfix! This is very cool! Also it's great the half-year version 2023.2 doesn't need 3rd party plugin rebuild and recompile as in S24, S26. Thanks for this!
  • 0 Votes
    2 Posts
    208 Views
    ferdinandF
    Hello @chuanzhen, Thank you for reaching out to us. Thank you for pointing this out, half of the word "offline" was pointing towards the offline C++ documentation. I have updated the link. Cheers, Ferdinand
  • Detect CTRL + RightMouseButton in SceneHook

    r25 c++ 2023
    3
    0 Votes
    3 Posts
    754 Views
    C4DSC
    Thanks for taking the time to respond, even during the weekend. I must say this is quite embarrassing: I already had forgotten about the response in the other thread, and it's not even 3 weeks old. I sincerely apologize for wasting your time with this duplicate thread. I can confirm the GetInputState is working fine, no need to waste more of your time trying it out. Thanks again.
  • Displaying tag properties as part of object's properties

    c++ maxon api sdk
    3
    1
    0 Votes
    3 Posts
    703 Views
    yesbirdY
    Thanks, @Manuel. The reason was in definition of TAG_MULTIPLE at the moment of registration. Now the tag properties are displayed as I was expecting. .... YB
  • 0 Votes
    3 Posts
    553 Views
    J
    Hi @ferdinand , First of all, I apologize for my poor explanation. I ran into an unexplainable situation, but your example solved my problem nicely. I couldn't get consecutive characters because I mistakenly called the self.GetInputState(c4d.BFM_INPUT_MOUSE, msg.GetInt32(c4d.BFM_INPUT_CHANNEL), msg) method and it would interrupt getting the original input. now I have solved the problem. Thank you very much you guys are the best! Cheers~
  • Python Generator use a script

    python 2023
    3
    0 Votes
    3 Posts
    500 Views
    K
    Thanks
  • Get Points and Polygons from Primitives

    2023 python c++
    2
    0 Votes
    2 Posts
    539 Views
    M
    Hi @CJtheTiger primitives are generators that generate and holds a PolyonObject in there cache. So most of the time for simple primitive like a cube, to access its generated PolygonObject, you should first retrieve it's cache with the GetCache method. If you want a more indepth introduction to this topic please read SDK Example - Geometry Readme. Then if you want to cover more complex cases I suggest you to read geometry_caches Example. And if you want to visualize the cache and the scene structure I would highly recommend to take a look at the C++ Example - activeobject dialog. Finally regarding the spline creation, you can find all the information needed to create a spline in Python Example - SplineObject, if you have more question on the spline creation please open a new topic. Cheers, Maxime.
  • Script: Connect + Delete all subdivision surface objects

    s26 python 2023
    4
    0 Votes
    4 Posts
    1k Views
    ferdinandF
    Hello @derekheisler, 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 follow the idea of keeping things short and mentioning your primary question in a clear manner. About your First Question Please excuse the slight delay and thank you for the community answer provided by @HerrMay. The core misconception in your code lies in the line objs = doc.GetObjects(), as it does not get a list of all objects in doc, but only the root level objects. Users are expected to do scene graph traversal on their own at the moment, there is no method which would do it for you. There are also other problems with your code, as for example trying to instantiate a BaseThread or using CallCommand (not a real problem but not recommended in most cases). There have been also multiple similar threads in the past: How do you collapse complex dependencies in order?: This is about implementing a connect and delete. How to traverse a GeListNode Tree?: This is about hierarchical traversal, i.e., getting all descendants and siblings for a node. There is also something that I would call branch traversal, it is much more complex and we have talked about it for example here. CAD Normal Tag flipped: Entirely different topic, but could give you an idea on how to realize a simple GUI for this. Finde below a simple example. Cheers, Ferdinand Result: [image: 1680193792635-collapse.gif] Code: """Demonstrates how to collapse all SDS objects in a scene. One could also make this more fancy with a GUI and undo steps, I did not do this here. """ import c4d doc: c4d.documents.BaseDocument # The active document. def IterNodes(node: c4d.GeListNode) -> c4d.GeListNode: """Yields all descendants and next-siblings of `node` in a semi-iterative fashion. """ if node is None: return while node: yield node # The recursion moving down. for descendant in IterNodes(node.GetDown()): yield descendant # The iteration in one level. node = node.GetNext() def Collapse(objects: list[c4d.BaseObject]) -> None: """Collapses all items in #objects as individual root nodes into singular objects. This function mimics the behaviour of the builtin (but unexposed) "Connect & Delete" command by first running the "CSTO" and then "JOIN" command. With setups complex enough, this can still fail due to the non-existent dependency graph of the classic API (when one does CSTO things in the wrong order). In 99.9% of the cases this will not be the case, but one should get the inputs with #GETACTIVEOBJECTFLAGS_SELECTIONORDER as I did below to give the user more control. (or alternatively do not batch operate). """ if len(objects) < 1: raise RuntimeError() doc: c4d.documents.BaseDocument = objects[0].GetDocument() doc.StartUndo() # CSTO all local hierarchies in #objects and replace these root nodes with their collapsed # counter parts. result = c4d.utils.SendModelingCommand(c4d.MCOMMAND_CURRENTSTATETOOBJECT, objects, c4d.MODELINGCOMMANDMODE_ALL, c4d.BaseContainer(), doc, c4d.MODELINGCOMMANDFLAGS_NONE) if not result or len(result) != len(objects): raise RuntimeError() for old, new in zip(objects, result): parent, pred = old.GetUp(), old.GetPred() doc.AddUndo(c4d.UNDOTYPE_DELETEOBJ, old) old.Remove() doc.InsertObject(new, parent, pred) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, new) # Join the CSTO results root by root object, and then replace the CSTO results with the final # collapsed result. JOIN is a bit weird when it comes to transforms, so we must store the # transform of the to be joined object, then zero it out, and finally apply it to the joined # result again. for obj in result: mg: c4d.Matrix = obj.GetMg() obj.SetMg(c4d.Matrix()) joined = c4d.utils.SendModelingCommand(c4d.MCOMMAND_JOIN, [obj], c4d.MODELINGCOMMANDMODE_ALL, c4d.BaseContainer(), doc, c4d.MODELINGCOMMANDFLAGS_NONE) if not joined: raise RuntimeError() parent, pred = obj.GetUp(), obj.GetPred() doc.AddUndo(c4d.UNDOTYPE_DELETEOBJ, obj) obj.Remove() new: c4d.BaseObject = joined[0] new.SetMg(mg) doc.InsertObject(new, parent, pred) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, new) doc.EndUndo() c4d.EventAdd() def main() -> None: """Runs the example. """ # Find all SDS objects in the scene. objectList: list[c4d.BaseObject] = [ n for n in IterNodes(doc.GetFirstObject()) if n.CheckType(c4d.Osds)] # Collapse all SDS objects we have found. for obj in objectList: # We have to check if #obj is still a valid reference because we could have already # collapsed the object away in a prior iteration; sds-objects can live inside sds-objects. if not obj.IsAlive(): continue # Collapse the object. Collapse([obj]) c4d.EventAdd() if __name__ == "__main__": main()
  • How to dynamically hide parameters

    python 2023
    2
    1
    0 Votes
    2 Posts
    608 Views
    ferdinandF
    Hello @kng_ito, Thank you for reaching out to us. Modifying the description of a node via NodeData.GetDDescription should only be done when NodeData.GetDEnabling is not sufficient. To hide a parameter, the field c4d.DESC_HIDE must be set to True. Find a simple example below. Cheers, Ferdinand Result: [image: 1680101928166-hide_param.gif] File: pc14487.zip Code: // The description defintion of the tag Texample. CONTAINER Texample { NAME Texample; INCLUDE Texpression; // The main "Tag" tab of the tag. GROUP ID_TAGPROPERTIES { // The element which is being hidden (or not). REAL ID_HIDDEN_ELEMENT { MIN 0.0; MAX 100.0; UNIT METER; STEP 0.001; CUSTOMGUI REALSLIDER; } // The parameter based on which the hidden element is shown or not. BOOL ID_HIDE_CONDITION {} } } """Implements a tag which dynamically hides and shows a parameter. """ import c4d import typing class ExampleTagData(c4d.plugins.TagData): """Implements a tag which dynamically hides and shows a parameter. """ # The plugin ID for the hook. ID_PLUGIN: int = 1060794 @classmethod def Register(cls) -> None: """Registers the plugin hook. """ if not c4d.plugins.RegisterTagPlugin( id=cls.ID_PLUGIN, str="Example Tag", info=c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, g=cls, description="texample", icon=c4d.bitmaps.InitResourceBitmap(c4d.Tdisplay)): print(f"Warning: Failed to register '{cls}' tag plugin.") def Init(self, node: c4d.GeListNode) -> bool: """Called to initialize a tag instance. Args: node: The BaseTag instance representing this plugin object. """ self.InitAttr(node, float, c4d.ID_HIDDEN_ELEMENT) self.InitAttr(node, bool, c4d.ID_HIDE_CONDITION) node[c4d.ID_HIDDEN_ELEMENT] = 50.0 node[c4d.ID_HIDE_CONDITION] = False return True def Execute(self, tag: c4d.BaseTag, doc: c4d.documents.BaseDocument, op: c4d.BaseObject, bt: c4d.threading.BaseThread, priority: int, flags: int) -> int: """Called when expressions are evaluated to let a tag modify a scene. Not used in this case. """ return c4d.EXECUTIONRESULT_OK def GetDDescription(self, node: c4d.GeListNode, description: c4d.Description, flags: int) -> typing.Union[bool, tuple[bool, int]]: """Called by Cinema 4D when the description of a node is being evaluated to let the node dynamically modify its own description. """ # Bail when the description cannot be not fully loaded. if not description.LoadDescription(node.GetType()): return False, flags # Define the ID of the parameter we want to modify and get the ID of the parameter which is # currently to be evaluated. paramId: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.ID_HIDDEN_ELEMENT, c4d.DTYPE_REAL, 0)) evalId: c4d.DescID = description.GetSingleDescID() # Bail when there is a to be evaluated parameter and our parameter is not part of or equal # to the evaluated parameter. if (evalId and not paramId.IsPartOf(evalId)): return True, flags # Get the description data container instance (GetParameter>I<) for the parameter we want to # modify. All changes made to the container will be directly reflected on the node. paramData: c4d.BaseContainer = description.GetParameterI(paramId) if paramData is None: return True, flags # Set the hidden state of the parameter ID_HIDDEN_ELEMENT based on the value of the # parameter ID_HIDE_CONDITION. paramData[c4d.DESC_HIDE] = node[c4d.ID_HIDE_CONDITION] return True, flags | c4d.DESCFLAGS_DESC_LOADED if __name__ == "__main__": ExampleTagData.Register()
  • 0 Votes
    4 Posts
    829 Views
    ferdinandF
    Hello @HerrMay, Is the multiplication of a matrix with a vector (mg.off = mg * p) essentially the same what mg * c4d.utils.MatrixMove(vec) does? No, mg * p multiplies a matrix by a vector which will yield the transformed vector p'. mg * c4d.utils.MatrixMove(vec) on the other hand multiplies two matrices, yielding the combined transform of them, a new matrix. What would be equivalent to mg * p is mg * c4d.utils.MatrixMove(p) * c4d.Vetcor(0, 0, 0). Please note that the whole subject is also out of scope of support as declared in the forum guidelines. I understand that the subject is a common barrier for the more on the artist-sided leaning users of ours, and I help where I can, but we cannot teach you vector math, linear algebra, or however you want to label this subject. Find below a short code example. Cheers, Ferdinand Output: M = Matrix(v1: (1, 0, 0); v2: (0, 1, 0); v3: (0, 0, 1); off: (0, 100, 0)) p = Vector(100, 0, 0) M * p = Vector(100, 100, 0) M * c4d.utils.MatrixMove(c4d.Vector(100, 0, 0)) = Matrix(v1: (1, 0, 0); v2: (0, 1, 0); v3: (0, 0, 1); off: (100, 100, 0)) M * c4d.Vector(100, 0, 0) = Vector(100, 100, 0) N * c4d.Vector( 0, 0, 0) = Vector(100, 100, 0) M * c4d.utils.MatrixMove(p) * c4d.Vector(0, 0, 0) = Vector(100, 100, 0) Code: import c4d # Let us assume #M to be the global matrix/transform of some object #op. View it as a tool to # transform a point in global space into the local coordinate system of #op. M: c4d.Matrix = c4d.Matrix(off=c4d.Vector(0, 100, 0)) # The matrix #M now has this form: # # | v1 1 0 0 | # The "x-axis" of the coordinate system defined by #M. # | v2 0 1 0 | # The "y-axis" of the coordinate system defined by #M. # | v3 0 0 1 | # The "z-axis" of the coordinate system defined by #M. # ---------------------- # | off 0 100 0 | # The origin of the coordinate system defined by #M. # # I.e., #M has the standard orientation and scale and only translates all things by 100 units # on the y-axis. So, the object #op would have the Euler angles (0°, 0°, 0°), the scale (1, 1, 1) # and the position (0, 100, 0) in world coordinates. # Define a vector #p to transform and print both #M and #p. p: c4d.Vector = c4d.Vector(100, 0, 0) print(f"{M = }") print(f"{p = }") # Transforming a point #p by a matrix #M will yield a point #q that is in the same relation to #M # as #p is to the global frame. q: c4d.Vector = M * p # #q will be the vector (100, 100, 0) because , (100, 100, 0) and #M are the same relation as #p # and the identity matrix are, a.k.a., the world frame. In a less technical way, (0, 100, 0) is the # origin of the coordinate system defined by #M. And to express #p in a manner as if #M would be # its coordinate system, we have to add (0, 100, 0), because that is the new origin. For orientation # and scale it works more or less the same, I would recommend having a look at the Matrix manual or # Wikipedia article on the subject. print (f"{M * p = }") # We can construct new transforms in many ways (again, Matrix Manual :)), one of them is by # combining multiple transforms via matrix multiplication. # We "add" the translation transform (100, 0, 0) to #M. Note that matrix multiplication is not # commutative, i.e., "M * N = N * M" does not always hold true. In this case it would because only # translations are involved. N: c4d.Matrix = M * c4d.utils.MatrixMove(c4d.Vector(100, 0, 0)) print(f"{M * c4d.utils.MatrixMove(c4d.Vector(100, 0, 0)) = }") # To get the same point as #q when multiplying a point #r with #N, we must pick the null-vector # because the origin of #N is already at where M * (100, 0, 0) is. print (f"{M * c4d.Vector(100, 0, 0) = }") print (f"{N * c4d.Vector( 0, 0, 0) = }") # We can also do it in one operation: print (f"{M * c4d.utils.MatrixMove(p) * c4d.Vector(0, 0, 0) = }")