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 script
-
RE: Complete Replica of a Native Objects UI?
There is a third-party module to serialize and deserialize the user data of any
BaseList2D
object 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.res
file 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.creator
to a specific value etc. -
RE: Complete Replica of a Native Objects UI?
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 python
@ferdinand
Thanks, you are the best. This is exactly the internal operation I'm finding. -
RE: C4D Parent constraint python
@ferdinand
The Constrain Tag may not only influence position, but also rotation, scale. I think theset inital state
button should be called as mentioned in my first reply:c4d.CallButton(tag, c4d.ID_CA_CONSTRAINT_TAG_SET_INITIAL_STATE) # SET INITIAL STATE
tag[30009,1000] = -parent.GetRelPos() #? tag[30009,1001] = -parent.GetRelScale() #? tag[30009,1002] = -parent.GetRelRot() #?
-
RE: C4D Parent constraint python
@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 python
@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
BaseTag
constructor, 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 Python
@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 Keyframe
@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 : 0
Then you can get the DescID:
c4d.DescID(c4d.DescLevel(1500, 19, 1018544))
-
RE: C4D Parent constraint python
Maybe you should call the
Set Inital State
button 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?
@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?
@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 creating
parameters setting
andports connecting
must 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 LoadAssets
@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) # 61
Then, 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 LoadAssets
My script works and I got what I want, but there are two problems.
-
maxon.AssetManagerInterface.LoadAssets
always 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 value
@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?!
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