Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. iplai
    3. Posts
    • Profile
    • Following 2
    • Followers 0
    • Topics 1
    • Posts 16
    • Best 4
    • Controversial 0
    • Groups 0

    Posts made by iplai

    • RE: HowTo set keyframes for PosXYZ/RotXYZ/ScaleXYZ with a script

      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.
      Snipaste_2023-01-12_06-08-50.png

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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)),
          },
      }
      

      Snipaste_2022-12-20_09-45-38.png
      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 restricting DescLevel.creator to a specific value etc.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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 input x * 1.25 .
      The document for Expanded Formula Entry by Multiple Selections is here:
      Snipaste_2022-12-18_04-33-07.png

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • RE: C4D Parent constraint python

      @ferdinand
      Thanks, you are the best. This is exactly the internal operation I'm finding.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • RE: C4D Parent constraint python

      @ferdinand
      The Constrain Tag may not only influence position, but also rotation, scale. I think the set 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() #?
      
      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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 using MakeTag, c4d will do some internal operations I guess:

      tag: c4d.BaseTag = child.MakeTag(c4d.Tcaconstraint)
      

      While using BaseTag constructor, then InsertTag, 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.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • RE: Mograph Objects Python

      @joel
      What you mentioned now is available in c4d 2023 version.
      Snipaste_2022-11-10_12-04-07.png
      Here is the link: Cinema 4D SDK 2023.1.0 documentation

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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).
      1.gif
      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))

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • RE: C4D Parent constraint python

      @mats

      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()
      
      
      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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)
      

      1.gif

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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 and ports 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.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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
      
      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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)
      
      

      Snipaste_2022-10-28_05-56-26.png

      posted in Cinema 4D SDK python s26
      iplaiI
      iplai
    • 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.

      posted in Cinema 4D SDK
      iplaiI
      iplai
    • 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.
      Snipaste_2022-09-28_17-31-18.png
      Cheers,
      Rui

      posted in General Talk
      iplaiI
      iplai