• Back on "message after tag delete" post

    python s26 sdk
    5
    0 Votes
    5 Posts
    1k Views
    mocolocoM
    Hi @ferdinand, Thanks a lot one more time for all the detailed examples and informations. I finally opt to a data container of the node, mostly due to the fact that the hooks are volatile and need to be set all the time. I also ran some tests with globals without having encounter issues, but indeed you need to be careful when handling this approach. Cheers, Christophe
  • How to make Icon buttons and Shortcut in python plugin

    python
    2
    0 Votes
    2 Posts
    506 Views
    ManuelM
    Hi, Welcome to the forum, you do not need to be sorry, we all ask basic question. There are differences between creating a script and plugins. Script is something you create inside the Script Manager. You can save them in a file and this file extension is .py. Plugins are something you must create in an external editor and save the file with the extension .pyp or .pypv (for encrypted files) with a certain plugin structure There are differents way to create an icon and a shortcut. icon for script: as state here, you can just use the command in the Script Manager to load a file that will be used as the icon for that script. If you save the script or already saved it, the picture will be saved in the same directory with the same name as the script. save the picture you want to use for a script in the same directory than your script with the same filename. icon for plugins: Plugins are registered using register commands like RegisterCommandPlugin, most of those commands allow you to pass as an argument the bitmap that will be used as the icon for that plugin. Shortcuts: To create shortcuts you must use the function AddShortcut you can check this thread where Maxime answer the question in the second part of his thread. Finally, you might want to create a palette to store your different icons and add the possibility to load the palette. Ferdinand answer this question in this thread. Cheers, Manuel
  • Building menus with C4DPL_BUILDMENU in S26+

    s26 python
    4
    0 Votes
    4 Posts
    2k Views
    ferdinandF
    Hello @alexandre-dj, I know all menus in C4D have their own id, so this should be an easy check. That is only true on a relatively abstract level. There are string symbols, e.g., IDS_EDITOR_PLUGINS for menu resources, but there is no strict namespace management or anything else on might associate with statements such as 'all menus in C4D have their own id'. Also note that all menu entries in a menu container are stored under the integer IDs MENURESOURCE_COMMAND and MENURESOURCE_SUBMENU as shown in my example above. So, when you have a menu with ten commands and four sub-menus, all fourteen items are stored under these two IDs. This works because BaseContainer is actually not a hash map as one might think, and can store more than one value under a key. Long story short - traversing and modifying menus is not ultra complicated but I would also not label it as 'easy' ; which is why I did provide the example in my previous posting. When firing c4d.gui.GetMenuResource("M_EDITOR") to Get the whole menu of Cinema 4D (as you mention in your code here) I get a different address every time: >>> c4d.gui.GetMenuResource("M_EDITOR") <c4d.BaseContainer object at 0x000001B406C86580> >>> c4d.gui.GetMenuResource("M_EDITOR") <c4d.BaseContainer object at 0x000001B406CA6A40> >>> c4d.gui.GetMenuResource("M_EDITOR") <c4d.BaseContainer object at 0x000001B406C88C80> You are calling here the Python layer for returning a Python object representation of the C++ object that represents the menu. Cinema 4D is still a C++ application and the menu exists in your memory with the layout defined by the C++ type BaseContainer. That type has memory-wise nothing to do with its Python counter part c4d.BaseContainer. So, the Python layer must create that Python data on the fly, as otherwise the memory-footprint of Cinema 4D would double (actually more, because Python data is not exactly sparse data) and synchronizing both data layers would bring Cinema 4D to a crawl. TLDR; you retrieve the same data expressed as a different memory object because 'that is how Python works'. So when I use c4d.gui.SearchMenuResource(main_menu_ressource, my_custom_menu) to check if my custom menu is part of the main menu, this always return False, as the address of the main_menu object changes. Why your call fails here, depends a bit on what you are passing. The first argument should be the string symbol inside a sub menu, and the second item is the sub menu. When you store a reference to a sub-menu from a previous call to GetMenuResource, this might not work when SearchPluginMenuResource searches by identity instead of equality, i.e., it expects that container to be at a certain memory location (have not tried though). There is also c4d.gui.SearchPluginMenuResource(identifier='IDS_EDITOR_PLUGINS'), which should be sufficient to find anything that does not produce a name collision. I personally would just write my own stuff as demonstrated above, since I can then search however I want to. Cheers, Ferdinand
  • Rendering into Multipassbitmap a "Depth Matte" Layer

    c++
    5
    0 Votes
    5 Posts
    871 Views
    ferdinandF
    Hey @wickedp, thank your for your reply. First of all, I don't expect you to do everything in case my answer gave the impression that I am unwilling to add such an example - that is not the case. I just must play a bit the gatekeeper for not letting our code examples become too convoluted and fringe. So, this was less a "ugh, we do not have time for this" than a "what demonstrates the general relevance of such example/information for most users?" thing. First of all, thank you for sharing what you would like to have documented as an example and your code. I personally would still say that this is an extremely specific example of a use case most users probably will never encounter. Which makes it a not so good example case. But we also have many fringe examples in our code base, and in the case one needs such example, it is quite helpful, as the parameter handling of bitmaps is a bit cryptic. I have added a task to our task pool to add such an example. I will add it in one of the next releases. Cheers, Ferdinand
  • Adding Layers to a MultipassBitmap

    c++
    2
    0 Votes
    2 Posts
    423 Views
    ManuelM
    Hi, If you want the layer and folder to be displayed in the picture viewer you need to define the parameter MPBTYPE_SAVE to True. MPBTYPE_SHOW is defined by default to true when you add a layer. This parameter will just disable the layer as seen below. (the red eye) [image: 1674808233490-7375d915-1e62-4b6b-b174-3a16d05a5e20-image.png] This example will create a folder with a layer in it and another layer below the folder. If it is working with pyhton, it should work with c++, you can share a bit of your code so we can reproduce it. 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: mpb = c4d.bitmaps.MultipassBitmap(640, 480, c4d.COLORMODE_ARGB) folder1 = mpb.AddFolder(None) folder1.SetParameter(c4d.MPBTYPE_NAME, "folder") folder1.SetParameter(c4d.MPBTYPE_SAVE, True) layer1 = folder1.AddLayer(None, c4d.COLORMODE_ARGB) layer1.SetParameter(c4d.MPBTYPE_NAME, "layer1") layer1.SetParameter(c4d.MPBTYPE_SAVE, True) layer2 = mpb.AddLayer(folder1, c4d.COLORMODE_ARGB) layer2.SetParameter(c4d.MPBTYPE_NAME, "layer2") layer2.SetParameter(c4d.MPBTYPE_SAVE, True) c4d.bitmaps.ShowBitmap(mpb) if __name__ == '__main__': main() Cheers, Manuel
  • Create RS Standard Material instead of RS Material

    2023 python
    6
    2
    0 Votes
    6 Posts
    965 Views
    P
    Ok, thank you.
  • Render settings Multi-Pass flag

    c++ sdk
    4
    1
    0 Votes
    4 Posts
    667 Views
    ferdinandF
    Hello @wickedp, I have forked your questions as they both did constitute new topics, find them here: Rendering into Multipassbitmap Depth Pass Adding Layers to a MultipassBitmap Cheers, Ferdinand
  • Inheritance created by Python script not getting saved

    python
    8
    0 Votes
    8 Posts
    1k Views
    J
    @ferdinand Thank you so much. Adding: inheritance1.Message(c4d.MSG_MEUPREPARE, doc) Solved the issue.
  • Changing DataType of a Value Node

    2023 python
    3
    1
    0 Votes
    3 Posts
    532 Views
    ManuelM
    Hi @bentraje, Nodes and ports are GraphNode. A GraphNode can store any kind of maxon Data. Using SetValue or SetDefaultValue on the "True Node" level will not change the value of a port. That is why you still need to find the port you want to change the value. SetDefaultValue internally encapsulate the value in a maxon.Data and use the SetValue function to define the value for the ID DESCRIPTION::DATA::BASE::DEFAULTVALUE. I do not see any advantage using SetValue instead of SetDefaultValue. While the GraphNode can receive any kind of Maxon Data, you can still define the DataType it should use. You must use the function SetValue to define the ID "fixedtype". In c++ you would do something like this: port.SetValue(nodes::FixedPortType, GetDataType<neutron::OBJECT_FLAGS>()) iferr_return;. Unfortunately, you cannot do it with python because you cannot define the datatype. If the Datatype is not defined, it will be deducted from the incoming or outgoing connection. In the case of the "Type" node, you are defining the port's value with this ID "net.maxon.parametrictype.vec<2,float>". This ID will allow the system to call the right CoreNode to manage this kind of DataType. The Datatype of this port is maxon::Id. I hope it is a bit clearer. I will try to add that to one of our manuals or in the documentation itself. Cheers, Manuel
  • Possible typo in documentation

    python
    3
    0 Votes
    3 Posts
    543 Views
    .
    Thanks for the info and adding the tag. I totally forgot that the Cafe' merged. I'm so used to approaching from my view. I actually answered somebodies question yesterday and at the very end saw the C++ tag and hit delete Thanks for tips on the GUI. I was able to implement what I needed.
  • How to get weights from a Vertex Map tag on an uneditable object

    2023 python
    5
    0 Votes
    5 Posts
    841 Views
    ManuelM
    @kng_ito said in How to get weights from a Vertex Map tag on an uneditable object: Sorry for asking a question that has already been resolved in another thread. Don't worry, we are glad to help. Cheers, Manuel
  • API for Adding a Port on a Group Node?

    2023 python
    5
    1
    0 Votes
    5 Posts
    780 Views
    B
    @manuel Thanks for the illustration. Works as expected. When you have this statement: maxon.GraphModelHelper.FindNodesByAssetId(graph,"net.maxon.node.type", True, value) valueNode = value[0] inputNode = valueNode.GetInputs().FindChild("in") I'm guessing this part of my previous code is no longer working and so we need to reestablish the variable again. value = selectedNodes[0] Anyhow, thanks again. Closing this thread now
  • Set the Preview of an Asset using an Image file.

    2023 python
    8
    0 Votes
    8 Posts
    1k Views
    ferdinandF
    Hello @tdapper, I still do not fully understand what you want to do, but in general, my answer does not change much. I do not want to be rude here, but in C++ you have everything you need. And what you want to do here, is fundamentally interfere with how the Asset API works. Such low-level access is traditionally the domain of the C++ API and not the Python API. Bottom line is that it's cool that the asset browser automatically creates thumbnails [...] That was less me showing off the features of the asset API and more pointing out its general approach. Therefore we are basically trying to replicate the functionality you get when you right-click on the thumbnail in the Asset Browser and Click "Update Thumbnail from File...". 'Update Thumbnail from File...' will not prevent thumbnails from being overwritten either. So the only way to do so, would be to make the metadata entry read-only as pointed out in my first posting. Perhaps there is a way to just immediately kill the job that creates the thumbnail from being generated in the background or prevent that job from starting in the first place. If that is not possible, maybe there is a message we could intercept to know the thumbnail has been updated so we can run our workaround right after the thumbnail creation job is finished instead of waiting a hardcoded amount of time. Maybe there is another option that we're not clearly seeing right now? Neither your first nor second option are possible. The preview thumbnail job queue is non-public and there is no such thing as messages in the maxon API. There are Observables which realize the idea of events, but they are not exposed in Python nor are you able to stop a preview rendering with them. Again, I do not want to be rude here, but as pointed out in my first posting, what you want to do is not intended. You could get hacky from the Python API, but that is more or less up to you. [image: 1674145517216-982bbbbf-ca43-4f16-a85c-b9b5cb836a6f-image.png] Fig. 1: The physical location of thumbnails for local assets is always the same, net.maxon.asset.previewimageurl.meta.png. With that knowledge one can infer the future pyhsical location of a thumbnail for a local asset. # Create a new object asset. asset: maxon.AssetDescription = maxon.AssetCreationInterface.CreateObjectAsset( obj, doc, storeAssetStruct, assetId, assetName, assetVersion, assetMetadata, True) # Get the physical location of the asset #asset, normally one should not touch # AssetDescriptionInterface.GetUrl(). Then get the directory of that path and infer the # preview thumbnail location from it. assetUrl: maxon.Url = asset.GetUrl() assetPath: maxon.Url = maxon.Url(assetUrl.GetSystemPath()) previewFile: maxon.Url = assetPath + maxon.Url("net.maxon.asset.previewimageurl.meta.png") # Print out the paths, #previewFile won't yet exist at this point, because the thumbnailing is # parallelized. print (f"{assetUrl = }") print (f"{assetPath = }") print (f"{previewFile = }") print (f"{os.path.exists(previewFile.GetUrl()) = }") Example output: assetUrl = file:///C:/Users/f_hoppe/AppData/Roaming/Maxon/2023.1.3_97ABE84B/userrepository/object_a2c9ff14d748481fb9a8ae03d7bfa9b7/1/asset.c4d assetPath = file:///C:/Users/f_hoppe/AppData/Roaming/Maxon/2023.1.3_97ABE84B/userrepository/object_a2c9ff14d748481fb9a8ae03d7bfa9b7/1/ previewFile = file:///C:/Users/f_hoppe/AppData/Roaming/Maxon/2023.1.3_97ABE84B/userrepository/object_a2c9ff14d748481fb9a8ae03d7bfa9b7/1/net.maxon.asset.previewimageurl.meta.png os.path.exists(previewFile.GetUrl()) = False With that knowledge you could start messing with that file. But that is obviously out of scope of support and a hack. Otherwise you will have to use the C++ API or file a bug report for your thumbnails being black in the first place. Cheers, Ferdinand
  • Unable to Retrieve the Input Port of Value Node

    2023 python
    3
    1
    0 Votes
    3 Posts
    432 Views
    B
    @manuel said in Unable to Retrieve the Input Port of Value Node: port = node.GetInputs().FindChild("in") Ah I gotcha. I always forget about displaying IDs in hte preferences since I'm hopping between old and newest versions of C4D. My bad. Anyhow, works as expected.
  • 0 Votes
    6 Posts
    709 Views
    ferdinandF
    Hey @thomasb, Thank you for the clarification. Yeah, this setup requires you modifying the cache. So, the slow performance version with disabling the optimization is the best you can do when approaching things in such brutish manner. FYI: I do not have much time this week, so this is all the help you will get this week from me, but I am happy to help you next week if you still need help then. Things you can do: Turning off the optimization will calculate the cache every time Cinema 4D asks for it. Depending on how your blinking works, you might not have to calculate the cache every frame. Just determine when a new cache is needed and when not, as demonstrated in my first posting. When in 99% of the cases 99% of your old invalid cache is still good, nothing prevents you from either caching expensive to compute parts yourself or modifying the existing cache and return that as the new one. Changing a selection state is such an example of where 99.9% of the expensive work is still valid. It would be quite easy to do, when selection tags could reach into caches, but they cannot. So, you cannot have a selection tag on a generator (in Python) which indexes elements of the cache. But you can have a selection tag inside the cache which is referenced by for example a material on the generator holding the cache. With this knowledge, you can: Write a solution following (2.) where everything happens in the object, but in most cases, you just modify an existing cache instead of creating a new one. Do the same, but here you use a tag to modify the cache. This is a little bit dicey, as you should not mess with caches. But in this specific form, where we only change the selection state of a selection tag inside the cache, it should be okay. All other external cache modifications are off limits and can lead to crashes when you do not know what you are doing. In a nicer variant, you would implement the tag as a TagData plugin, but I provided a simple Python programming tag version below. The shader solution is not viable in Python; it will be too slow. You will also need quite some math knowledge and reverse engineering skills, as you would have to sort of reimplement texture mapping. PS: In your more complex setup, this could mean that you just change the materials on things. Although consolidating things inside caches is always advantageous. The more generators your cache contains, the more expensive it will be to evaluate the cache of your object. When possible, it is always better to return a single polygon object as your cache result, or at least a tree which contains only null objects and polygon objects, and no generator objects as the Sphere object, instance-objects, cloners, etc., i.e., things which must be cached themselves. When the cache for an object is being built, all that stuff is converted to polygons anyway. But when you return one hundred instance objects which reference a sphere generator each, cinema will have to build 102 caches in total: one for your object, one for the sphere object, and one hundred for the instance objects. When you just return one polygon object which contains all the geometry, Cinema 4D must build only one cache. In Python this fact is a little bit mitigated by the slowness of Python, and it can be advantageous to push things to C++, but your cache is too complicated IMHO. Just construct your LED once, then build the cache for it, copy the cache thirty-five times, modify the position and material of each copy, and return these thirty-five copies under a null object as your object cache. Cheers, Ferdinand File: led.c4d Result:[image: 1674038759398-led_ani.gif] Python Generator object: import c4d op: c4d.BaseObject # The Python Generator object containing this code. def main() -> c4d.BaseObject: """Returns a clone of the polygon object linked in its first user data field. """ source: c4d.PolygonObject = op[c4d.ID_USERDATA, 1] if not isinstance(source, c4d.PolygonObject): return c4d.BaseObject(c4d.Onull) clone: c4d.PolygonObject = source.GetClone(c4d.COPYFLAGS_NO_HIERARCHY | c4d.COPYFLAGS_NO_BITS) clone.SetMg(c4d.Matrix()) return clone Python Programming tag: import c4d doc: c4d.documents.BaseDocument # The document evaluating this tag. op: c4d.BaseTag # They Python Programming tag containing this code. def main(): """Reaches into the cache of its host object and modifies it. """ # Get the host object, its cache, and find the polygon selection tag on it. obj: c4d.BaseObject = op.GetMain() if not isinstance(obj, c4d.BaseObject): return cache: c4d.BaseObject = obj.GetCache() if not isinstance(cache, c4d.PolygonObject): return tag: c4d.SelectionTag = cache.GetTag(c4d.Tpolygonselection) if not isinstance(tag, c4d.SelectionTag): return # Get the current document frame and the selection of the tag and flush it. In practice you could # also make this parameter driven, but for expressions, tags, it is also fine to make things # automatic as such. frame: int = doc.GetTime().GetFrame(doc.GetFps()) bs: c4d.BaseSelect = tag.GetBaseSelect() bs.DeselectAll() # Define the indices of 100 cap polygons, and pick the polygon which matches the current time. states: list[int] = [n for n in range(4, 599, 6)] i: int = states[frame % 100] # Set the new selected element. bs.Select(i)
  • Make Description Parameter Uneditable

    r23 2023 python windows
    3
    1
    0 Votes
    3 Posts
    515 Views
    ThomasBT
    @ferdinand As always thank you very much Ferdinand, it works as expected
  • A few items that should get fixed on the Python side

    2023 python
    6
    0 Votes
    6 Posts
    1k Views
    M
    @ferdinand said in A few items that should get fixed on the Python side: Hey @mikegold10, ..., the word "of" does not in any way indicate ownership, [...] It does, of is a possessive preposition, in is a relative preposition, specifically one that denotes a place. See here for an overview of the subject. I would not even challenge the fact that for you and your peers of might sound more natural (or maybe even all native speakers as you claim). But language in general, and especially things like prepositions, pronouns, and flexions, is subject to language change which is often highly regional, so what people consider correct can vary regionally. As indicated in my first answer, prepositions are quite interchangeable, and if the text would have said of instead of in, I would not waste any time on changing that. But in the same notion, I cannot change the preposition on what you subjectively consider better. The grammatical situation from a Standard English point of view seems obvious here and personal preference cannot be grounds for a change. I am more than happy to change the preposition when you can demonstrate with a respectable English style guide like Oxford English that it would be more common to use here of. But that seems unlikely to me. Cheers, Ferdinand As additional supportive evidence for my proposed change, I am providing a link to a respected source for the definitions of American English words, including many sample phrases that provide context for the often huge set of alternative definitions that are provided for commonly used words like "of" (and "in"): Meriam-Webster Dictionary: of - Definition / preposition Specifically, under the definitions of the word "of," when used as a preposition (i.e., Section 1 of 3 on the page, as linked above), please examine the following alternative definitions and examples of proper English usage that I believe are of relevance to my proposed change to the original Python comment: a, c, and e a and b b Here is a copy of the original comment to provide context: The index of the first and last selected elements in the given segment. ..., and the revised version that replaces the preposition in with of:: The index of the first and last selected elements of the given segment. Of course the decision is yours to make. I am simply trying to "open your mind" to, as well as provide objective evidence for, an alternative phrasing which I perceive to be a better fit for the technical subject matter that is being documented, within the framework of modern American English usage patterns. I have to concede on the fact that this is ultimately subjective and may (or may not - not sure) be specific to the en_US locale. Michael
  • RS Proxy Export Parameters?

    2023 python
    2
    1
    0 Votes
    2 Posts
    284 Views
    B
    False Alarm. Found it under C:\Program Files\Maxon Cinema 4D 2023\Redshift\res\description instead. Hehe. Closing this thread now.
  • HowTo set keyframes for PosXYZ/RotXYZ/ScaleXYZ with a script

    17
    1
    0 Votes
    17 Posts
    3k Views
    ferdinandF
    Hey @vannipo, GUI builder is inside C4D, isn't it? I am not sure, atm. There is no WYSIWYG GUI builder for dialogs in Cinema 4D anymore. A long time ago existed the classic API Resource Editor, but it is not published anymore by us. The Resource Editor you can find via CTRL + C in a modern Cinema 4D instance is an editor for maxon API resources, a different and newer GUI paradigm in Cinema 4D. When you want to implement a dialog, you will have to either write a resource file manually or use the methods of GeDialog to add elements at runtime, you could have a look at this posting where I recently lined out some basics. The Python GUI Manual is quite superficial at the moment, but we have some simple examples on GitHub. For learning dialog resource markup, I would recommend the Dialog Resource Manual. The most important point for an animator is to key the desired channel as fast as possible. [...] But, the pop-up idea is very interesting, and I will have a look [...] Yeah, I understood that you were after a very streamlined setup. When you want to make zero compromises, nine shortcuts are probably the best solution. The solution I proposed with the popup menu is a compromise of the number of shortcut keys to allocate and the speed with which keys can be generated. The advantage is here that the popup menu will always open under your mouse cursor no matter where it is, which will minimize mouse travel distances. But when you overpopulate the menu with all nine entries (you can also add separators if you want to), selecting the right item will probably become slow. So, if you want a good compromise, you could use three scripts (translate/rotate/scale) which each provide a popup for x, y, or z. Btw, is it possible to set a "real name" for a script? atm, everywhere it just shows the filename. You can supply a different name from its filename by adding file docstring containing Name-en-US: XXXX where XXX is the name of the plugin. This can also be done for description that will be displayed when you hover the command with Description-en-US: XXX. If you need to support other language please refer to Plugin Structure Manual which specify the different language code. Finally you can find an example within GitHub - script_custom_name_description.py Cheers, Ferdinand
  • 0 Votes
    8 Posts
    1k Views
    P
    Thanks for the good explanation.