Hello @vannipo ,
What you need may not a plugin, scripts is enough. In Command Manager, search your script's name, then assign the shortcut you like. Assign a shortcut key to each of your nine scripts, You can even drag the item out of the list tree view and turn it into a button.

Posts made by iplai
-
RE: HowTo set keyframes for PosXYZ/RotXYZ/ScaleXYZ with a scriptposted in Cinema 4D SDK
-
RE: Complete Replica of a Native Objects UI?posted in Cinema 4D SDK
There is a third-party module to serialize and deserialize the user data of any
BaseList2Dobject in python dict format. Here's an example:c4d.ID_USERDATA: { (c4d.ID_USERDATA, 1): { c4d.DTYPE_: c4d.DTYPE_REAL, c4d.DESC_NAME: 'Data', c4d.DESC_SHORT_NAME: 'Data', c4d.DESC_MIN: 0, c4d.DESC_MAX: 1, c4d.DESC_STEP: 0.01, c4d.DESC_UNIT: c4d.DESC_UNIT_PERCENT, c4d.DESC_CUSTOMGUI: c4d.CUSTOMGUI_REAL, c4d.DESC_PARENTGROUP: (700, 5, 0), }, (c4d.ID_USERDATA, 2): { c4d.DTYPE_: c4d.DTYPE_GROUP, c4d.DESC_NAME: 'Group', c4d.DESC_SHORT_NAME: 'Group', c4d.DESC_ANIMATE: c4d.DESC_ANIMATE_OFF, c4d.DESC_COLUMNS: 1, c4d.DESC_TITLEBAR: 1, c4d.DESC_DEFAULT: 1, c4d.DESC_PARENTGROUP: (), }, (c4d.ID_USERDATA, 3): { c4d.DTYPE_: c4d.DTYPE_LONG, c4d.DESC_NAME: 'Data', c4d.DESC_SHORT_NAME: 'Data', c4d.DESC_UNIT: c4d.DESC_UNIT_INT, c4d.DESC_CUSTOMGUI: c4d.CUSTOMGUI_LONGSLIDER, c4d.DESC_MIN: 0, c4d.DESC_MAX: 100, c4d.DESC_STEP: 1, c4d.DESC_PARENTGROUP: ((700, 5, 0), (2, 1, 0)), }, }
The module is not fully tested, but it's enough for me for my daily scripting work. The processing of parameters is implemented in a way described by @ferdinand . Sort of like the.resfile used by plugins developing, the json data defines the details of a parameter. If you feel like it, you can implement it yourself by refering to following functions:
DumpParams
DumpParamDetails
DumpUserDataDetails
LoadUserData
...
Considering sometimes there's a huge amount of parameters, I spent a lot of time to judge and handle dirty parameters, namely the parameter has been changed, there is still no perfect way. If you don't care whether the parameter is dirty, problem goes easier. Maybe do some filters by restrictingDescLevel.creatorto a specific value etc. -
RE: Complete Replica of a Native Objects UI?posted in Cinema 4D SDK
Hi, @bentraje
For each parameter input box, formula expressions are supported in c4d,x: represents the parameter's current value. If you want to change the intensity by 125% than their current value for each light, you can select all of them and simply inputx * 1.25.
The document for Expanded Formula Entry by Multiple Selections is here:

-
RE: C4D Parent constraint pythonposted in Cinema 4D SDK
@ferdinand
Thanks, you are the best. This is exactly the internal operation I'm finding. -
RE: C4D Parent constraint pythonposted in Cinema 4D SDK
@ferdinand
The Constrain Tag may not only influence position, but also rotation, scale. I think theset inital statebutton should be called as mentioned in my first reply:c4d.CallButton(tag, c4d.ID_CA_CONSTRAINT_TAG_SET_INITIAL_STATE) # SET INITIAL STATEtag[30009,1000] = -parent.GetRelPos() #? tag[30009,1001] = -parent.GetRelScale() #? tag[30009,1002] = -parent.GetRelRot() #? -
RE: C4D Parent constraint pythonposted in Cinema 4D SDK
@ferdinand
I verified the scene again, the two methods do the same thing indeed. Forgive my mistakes last post.
The problem is when the parent is not at original position, after apply the script, the offset arises.
The selection order might also affect the result. I'm trying to figure it out. -
RE: C4D Parent constraint pythonposted in Cinema 4D SDK
@ferdinand @Mats
I compared the two pieces of code and found the key diffence is the lines of creating the Constraint Tag:
When you usingMakeTag, c4d will do some internal operations I guess:tag: c4d.BaseTag = child.MakeTag(c4d.Tcaconstraint)While using
BaseTagconstructor, thenInsertTag, no internal operations are done:tag = c4d.BaseTag(c4d.Tcaconstraint) child.InsertTag(tag)In my experience, for the most cases of tags, both of them work properly, apart from the above exception.
-
RE: Mograph Objects Pythonposted in Cinema 4D SDK
@joel
What you mentioned now is available in c4d 2023 version.

Here is the link: Cinema 4D SDK 2023.1.0 documentation -
RE: Python Cloner Effectors Keyframeposted in Cinema 4D SDK
@joel
The simplest way is to drag the parameter to the python console, then you could get desclevel id without dtype and creator. e.g.c4d.DescID(1500).

If you want to get the DescID programmatically. I wrote a function to list all parameters and detail descriptions of an object. This may help you.import c4d def find_ident(value: int, prefix=""): for key, val in c4d.__dict__.items(): if val == value and key.startswith(prefix): return f"c4d.{key}" def dump_params(obj: c4d.BaseList2D): def dump_param_desc(bc): for k, v in bc: ident = find_ident(k, "DESC_") if type(v) == str: v = repr(v) if not ident: continue if k == c4d.DESC_UNIT: v = find_ident(v, "DESC_") if k == c4d.DESC_CUSTOMGUI: v = find_ident(v, "CUSTOMGUI") or v if isinstance(v, c4d.Vector) and abs(v.x) > 1e10: v = "Vector" print('\t', ident, ":", v) description = obj.GetDescription(c4d.DESCFLAGS_DESC_0) descid: c4d.DescID for bc, descid, _ in description: name = bc[c4d.DESC_NAME] or bc[c4d.DESC_SHORT_NAME] if not name: continue if descid[descid.GetDepth() - 1].dtype == c4d.DTYPE_GROUP: print("---------") print(name, descid, ":", repr(obj[descid])) dump_param_desc(bc) if bc[c4d.DESC_CUSTOMGUI] == c4d.CUSTOMGUI_SUBDESCRIPTION: for subvector in [c4d.VECTOR_X, c4d.VECTOR_Y, c4d.VECTOR_Z]: subdescid = c4d.DescID(*[descid[i] for i in range(descid.GetDepth())]) subdescid.PushId(c4d.DescLevel(subvector)) subbc = description.GetParameter(subdescid) name = subbc[c4d.DESC_NAME] or subbc[c4d.DESC_SHORT_NAME] if not name: continue name = name.replace(" . ", ".") print(name, ":", obj[subdescid]) dump_param_desc(subbc) if __name__ == "__main__": obj = doc.GetActiveObject() dump_params(obj)Following is the outputs to the end:
Effectors (2009, 1009290, 1018639) : <c4d.InExcludeData object at 0x00000198DD88E9C0> c4d.DESC_NAME : 'Effectors' c4d.DESC_SHORT_NAME : 'Effectors' c4d.DESC_VERSION : 3 c4d.DESC_ANIMATE : 0 c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_INEXCLUDE_LIST c4d.DESC_ACCEPT : <c4d.BaseContainer object at 0x00000198DD88DE00> c4d.DESC_IDENT : 'ID_MG_MOTIONGENERATOR_EFFECTORLIST' Plain (1500, 19, 1018544) : 0.99 c4d.DESC_NAME : 'Plain' c4d.DESC_SHORT_NAME : 'Plain' c4d.DESC_VERSION : 3 c4d.DESC_MIN : -1e+20 c4d.DESC_MAX : 1e+20 c4d.DESC_MINEX : 0 c4d.DESC_MAXEX : 0 c4d.DESC_STEP : 0.01 c4d.DESC_ANIMATE : 1 c4d.DESC_UNIT : c4d.DESC_UNIT_PERCENT c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_REALSLIDER c4d.DESC_MINSLIDER : 0.0 c4d.DESC_MAXSLIDER : 1.0 c4d.DESC_REMOVEABLE : 0 Plain.1 (1501, 19, 1018544) : 1.0 c4d.DESC_NAME : 'Plain.1' c4d.DESC_SHORT_NAME : 'Plain.1' c4d.DESC_VERSION : 3 c4d.DESC_MIN : -1e+20 c4d.DESC_MAX : 1e+20 c4d.DESC_MINEX : 0 c4d.DESC_MAXEX : 0 c4d.DESC_STEP : 0.01 c4d.DESC_ANIMATE : 1 c4d.DESC_UNIT : c4d.DESC_UNIT_PERCENT c4d.DESC_CUSTOMGUI : c4d.CUSTOMGUI_REALSLIDER c4d.DESC_MINSLIDER : 0.0 c4d.DESC_MAXSLIDER : 1.0 c4d.DESC_REMOVEABLE : 0Then you can get the DescID:
c4d.DescID(c4d.DescLevel(1500, 19, 1018544)) -
RE: C4D Parent constraint pythonposted in Cinema 4D SDK
Maybe you should call the
Set Inital Statebutton of the tag and set the local offset vector as the target's negative vector.
Here's the code:import c4d objList = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER | c4d.GETACTIVEOBJECTFLAGS_CHILDREN) tag = c4d.BaseTag(c4d.Tcaconstraint) obj1, obj2 = objList[:2] obj1.InsertTag(tag) c4d.CallButton(tag, c4d.ID_CA_CONSTRAINT_TAG_SET_INITIAL_STATE) # SET INITIAL STATE tag[c4d.ID_CA_CONSTRAINT_TAG_PARENT] = True tag[30009, 1000] = obj2[c4d.ID_BASEOBJECT_REL_POSITION] * -1 # Local Offset P tag[30001] = obj2 # Target c4d.EventAdd() -
RE: Paste JSON Values on the Node Network?posted in Cinema 4D SDK
@bentraje
I misunderstood your question in my previous reply.
This is my trial:import c4d with open(r"Path\to\your\file.json") as f: c4d.CopyStringToClipboard(f.read()) # Paste command in Node Editor # Command ID can be got by Script Log c4d.CallCommand(465002305)
-
RE: Paste JSON Values on the Node Network?posted in Cinema 4D SDK
@bentraje
I wrote the c4djson module to do what you described for ordinary c4d objects, but not for xpresso and node network(Neutron and node material). If it's impletmented, the json format maybe like this:{ "Node Graph Root": { "Node Identifier 1": { "Attr1": "Value1", "Attr2": "Value2", ... "In Ports": { "Port 1": {}, "Port 2": {}, ... }, "Out Ports": { "Port 1": {}, "Port 2": {}, ... }, }, "Node Identifier 2": {...}, ... "Connections": [ (OutPort, InPort), (OutPort, InPort), ... ] } }There's not a decent way for the parser to define or find the ports. And some parameters dynamicly occur when connections made. So, the sequence of
node creatingparameters settingandports connectingmust be carefully handled.Then too much more information is needed for node system, making things more complex.This goes against my original vision. From a certain point of view, the node system is essentially a kind of programming language, using json to serialize it seems stupid and superfluous. -
RE: Geometry Axis asset node and LoadAssetsposted in Cinema 4D SDK
@manuel
Thank you very much to answer my confusion. If I do not need to pass a graphModelRef to the LoadAssets function, my code can be simplified a lot. Can it be okay to identify the parameter by the index of iterating the capsule's description. Since the capsule is newly created, and there're no extra parameters, so it seems the index is fixed.First, get the index by following code.
for i, (bc, descid, _) in enumerate(capsule.GetDescription(c4d.DESCFLAGS_DESC_0)): name = bc[c4d.DESC_NAME] if name == "Axis X": print(i) # 59 if name == "Axis Y": print(i) # 60 if name == "Axis Z": print(i) # 61Then, the total function def:
def loadGeometryAxis(x=0, y=0, z=0, parent: c4d.BaseObject = None): repository = maxon.AssetInterface.GetUserPrefsRepository() if not repository: raise RuntimeError("Could not access the user preferences repository.") # Geometry Axis asset id assetid = maxon.Id("net.maxon.neutron.asset.geo.geometryaxis") assetsToLoad = [(assetid, ""), ] maxon.AssetManagerInterface.LoadAssets(repository, assetsToLoad) capsule = doc.GetFirstObject() for i, (bc, descid, _) in enumerate(capsule.GetDescription(c4d.DESCFLAGS_DESC_0)): if i == 59: capsule[descid] = x if i == 60: capsule[descid] = y if i == 61: capsule[descid] = z if parent is not None: capsule.InsertUnderLast(parent) return capsule -
Geometry Axis asset node and LoadAssetsposted in Cinema 4D SDK
My script works and I got what I want, but there are two problems.
-
maxon.AssetManagerInterface.LoadAssetsalways return False event the asset loaded successfully. -
I set the parameters of Geometry Axis by find the param's name. Is there a better way to change the parameters?
import maxon, c4d def loadGeometryAxis(x=0, y=0, z=0, parent: c4d.BaseList2D = None): repository = maxon.AssetInterface.GetUserPrefsRepository() if not repository: raise RuntimeError("Could not access the user preferences repository.") # Geometry Axis asset id assetid = maxon.Id("net.maxon.neutron.asset.geo.geometryaxis") assetsToLoad = [(assetid, ""), ] sceneNodesHook = doc.FindSceneHook(c4d.SCENENODES_IDS_SCENEHOOK_ID) if not sceneNodesHook: raise RuntimeError("Could not retrieve Scene Nodes scene hook.") sceneNodesHook.Message(maxon.neutron.MSG_CREATE_IF_REQUIRED) sceneNodes = sceneNodesHook.GetNimbusRef(maxon.neutron.NODESPACE) if not sceneNodes: raise RuntimeError("Could not retrieve Scene Nodes graph model.") graph = sceneNodes.GetGraph() didLoad = maxon.AssetManagerInterface.LoadAssets(repository, assetsToLoad, graphModelRef=graph) if not didLoad: # `didLoad` is always False, event the asset loaded successfully. # What's wrong? # raise RuntimeError(f"Could not load assets for the ids: {assetsToLoad}") ... obj = doc.GetFirstObject() # Is there is a better way to change the parameters? for bc, descid, _ in obj.GetDescription(c4d.DESCFLAGS_DESC_0): name = bc[c4d.DESC_NAME] if name == "Axis X": obj[descid] = x if name == "Axis Y": obj[descid] = y if name == "Axis Z": obj[descid] = z if parent is not None: obj.InsertUnderLast(parent) if __name__ == "__main__": cube = c4d.BaseObject(c4d.Ocube) loadGeometryAxis(y=1, parent=cube) doc.InsertObject(cube)
-
-
RE: Python. Cinema4d. All iterations in the Text valueposted in Cinema 4D SDK
@smetk
You need line break character to join the items.from random import random def main() -> None: global Output1 Output1 = "\n".join(f"{random()*10:.6f}" for i in range(6))Cheers.
-
RE: Slow MoGraph in 2023?!posted in General Talk
The issue is the change of default behavior of Cloner Generator Instance Mode in R2023, if you set it to Render Instance or Multi-Instance, the FPS will rise back as in R26.

Cheers,
Rui