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

    Trouble with GetAndCheckHierarchyClone

    Cinema 4D SDK
    2
    8
    1.3k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • matniedobaM
      matniedoba
      last edited by matniedoba

      Hey folks,

      I could need your help on building a Python Generator version of the Chamfer Tool. It's based on the Py-Parametric tools build by Andreas back in days. There were very helpful for setting up the generator. But here comes the issue:
      Using GetAndCheckHierarchyClone does not work well with the MCOMMAND_CURRENTSTATETOOBJECT (at least this is what I think). When I test the plugin and use a Cloner with Splines under a Connect Object, it only affects the first clone. When I make the Connect Object editable, it works on all objects. See images.
      When I bypass the dirty check and add replace "clone" by "op" in line 63 in the "GetVirtualObjects" method, than it works, but very badly.

      I would appreciate any kind of help 🙂

      import os
      import c4d
      from c4d import plugins, utils, bitmaps
      
      ###############################################################################################################
      #Based on the Py-Parametric Tools by Andreas Block
      ###############################################################################################################
      
      class ModelingCommandGeneratorModifier(plugins.ObjectData):
          _toolId = -1
          _doSelection = False
          _selectId = -1
      
          def InitCommon(self, toolId, doSelection, selectId):
              self._toolId = toolId
              self._doSelection = doSelection
              self._selectId = selectId
      
          def ExecModelingCommand(self, doc, opCtrl, op, parent):
              if op is None:
                  return
              
              splineObjects = []
              if op.GetDown().CheckType(c4d.Ospline) is False:
                  splineObjects = utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,
                                      list = [op.GetDown()],
                                      mode = c4d.MODELINGCOMMANDMODE_ALL,
                                      bc = c4d.BaseContainer(),
                                      doc = doc)
              else:
                  splineObjects.append(op.GetDown())
      
              if not splineObjects:
                  return
                  
              if splineObjects[0].CheckType(c4d.Ospline) is True:
                  res = utils.SendModelingCommand(command = self._toolId,
                                                  list = splineObjects,
                                                  mode = c4d.MODELINGCOMMANDMODE_ALL,
                                                  bc = opCtrl.GetDataInstance(), # settings,
                                                  doc = doc)
                  if res is True:
                      splineObjects[0].InsertUnderLast(parent)
              
          def GetVirtualObjects(self, op, hh):
              doc = op.GetDocument()
              objInput = op.GetDown()
              
              if objInput is None:
                  return None
              
              objRet = c4d.BaseObject(c4d.Onull)
              if doc is None or objRet is None:
                  return None
              hierarchyClone = op.GetAndCheckHierarchyClone(hh, objInput, c4d.HIERARCHYCLONEFLAGS_NONE, True)
              if hierarchyClone["dirty"] is False:
                  return hierarchyClone["clone"]
              clone = hierarchyClone["clone"]
              if clone is None:
                  return objRet
              
              #When I replace "clone" to "op" and bypass the dirty checks, than it works but very sluggish
              self.ExecModelingCommand(doc, op, clone, objRet)
              return objRet
      
      class ChamferGen(ModelingCommandGeneratorModifier):
          def Init(self, op):
              ModelingCommandGeneratorModifier.InitCommon(self, c4d.ID_MODELING_SPLINE_CHAMFER_TOOL, False, -1)
              InitChamferDesc(self, op)
              return True
      
      def InitChamferDesc(inst, op):
          inst.InitAttr(op, bool, [c4d.MDATA_SPLINE_CHAMFERFLAT])
          inst.InitAttr(op, float, [c4d.MDATA_SPLINE_CHAMFERRADIUS])
          
          op[c4d.MDATA_SPLINE_CHAMFERRADIUS] = 5.0
      
      
      ###############################################################################################################
      # Plugin Registration
      ###############################################################################################################
      
      PLUGIN_ID_GENERATOR = 9036026
      
      
      
      def RegisterObjectData(id, name, bmpPath, objData, desc, flags):
          bmp = bitmaps.BaseBitmap()
          bmp.InitWith(os.path.join(bmpPath, "res", "icon.tif"))
          plugins.RegisterObjectPlugin(id=id, str=name,
                                      g=objData,
                                      description=desc, icon=bmp,
                                      info=flags)
      
      if __name__ == "__main__":
          path, fn = os.path.split(__file__)
      
          RegisterObjectData(PLUGIN_ID_GENERATOR, "ChamferGen", path, ChamferGen, "ochamfergen", c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT)
      
          print "ChamferGen 1.0 successfully initialized"
      
      

      Screenshot 2020-02-13 at 18.43.51.png
      Screenshot 2020-02-13 at 18.44.03.png

      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by Manuel

        hello,
        please use our forum functionalities 🙂

        • Q&A New Functionality.

        as said in the documentation, Current State to Object should be use on a clone. And using a temporary document can help.

        If you were not using a cloner, you could use HIERARCHYCLONEFLAGS_ASSPLINE. But with a cloner, the objects are returned as lineObject. And the chamfer only accept SplineObject (it checks for Ospline type).

        To avoid to run through the hierarchy, i've used a Join command and using the chamfer on the result.

            def GetVirtualObjects(self, op, hh):
            
                objRet = c4d.BaseObject(c4d.Onull)
                doc = op.GetDocument()
                if doc is None:
                    return objRet
                objInput = op.GetDown()
                
                if objInput is None:
                    return objRet
        
                # If we set the flags to HIERARCHYCLONEFLAGS_ASSPLINE the result will be a lineObject and not a SplineObject. This will not pass the Chamfer tool.
                hierarchyClone = op.GetAndCheckHierarchyClone(hh, objInput, c4d.HIERARCHYCLONEFLAGS_NONE , True)
                if hierarchyClone["dirty"] is False:
                    return hierarchyClone["clone"]
        
                # Retrieves the cloner
                cloner = op.GetDown()
                # Creates a temporary document.
                tempDoc = c4d.documents.BaseDocument()
                # Use a clone of the cloner so we can send it to a temporary document.
                cloned = cloner.GetClone(c4d.COPYFLAGS_NONE)
                # Insert the cloned object to the temporary document.
                tempDoc.InsertObject(cloned)
                # Retrieves the currect states.
                result = utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,
                                        list = [cloned]
                                        , doc= tempDoc)
        
                spline = None
                if result:
                    spline = result[0].GetDown()
                else:
                    return objRet
                # Use the join command so we only have one spline object. (that help in case of a hierarchy)
                result = utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                        list = result
                                        , doc= tempDoc)
        
                # Champher the spline
                if result[0].IsInstanceOf(c4d.Ospline):
                    bc = c4d.BaseContainer()
                    bc[c4d.MDATA_SPLINE_CHAMFERFLAT] = True
                    bc[c4d.MDATA_SPLINE_CHAMFERRADIUS] = 19.0
                    
                    res = utils.SendModelingCommand(command = c4d.ID_MODELING_SPLINE_CHAMFER_TOOL,
                                            list = result,
                                            bc = bc)
        
                if res:
                    return result[0]
                
                return objRet
        

        Cheers,
        Manuel.

        MAXON SDK Specialist

        MAXON Registered Developer

        matniedobaM 1 Reply Last reply Reply Quote 1
        • matniedobaM
          matniedoba @Manuel
          last edited by

          @m_magalhaes

          Thank you for your help! It works. I really like the tip of the temporary document. Does it make sense to use c4d.documents.KillDocument(doc) after the process is complete to free up resources?

          1 Reply Last reply Reply Quote 0
          • matniedobaM
            matniedoba
            last edited by matniedoba

            I also have another question. How should I change the setup to support multiple children? (see image) I tried to insert multiple objects in the tempDoc via a loop but nothing really worked.

            Screenshot 2020-02-16 at 15.32.47.png

            1 Reply Last reply Reply Quote 0
            • ManuelM
              Manuel
              last edited by

              hello,

              you can retrieve the hierarchy clone "asis" with

               hierarchyClone = op.GetAndCheckHierarchyClone(hh, objInput, c4d.HIERARCHYCLONEFLAGS_ASIS , True)
              

              Than use Currentstate to object and join the result as with the cloner.

              in my previous code you can just change this line to see the result.

               cloner = hierarchyClone["clone"]
              

              The problem is that if you have a polygon object, the join command will return a polygon object and a spline.
              One workaround would be to run through the hierarchy and remove the polygon objects.

              Just an idea, have you tried a deformer instead of a generator ? (just like our bevel deformer for polygon object)

              Cheers,
              Manuel

              MAXON SDK Specialist

              MAXON Registered Developer

              1 Reply Last reply Reply Quote 0
              • matniedobaM
                matniedoba
                last edited by matniedoba

                Hey Manuel,

                I tried this method but I encountered another issue. The copy to another document does not work in a setup with a Mograph Effector. Therefore, I am inserting the object to the original document and try to remove the original. But that does not work. When I make ChamferGen editable, it creates the original Connect+Children objects as well as the editable Spline Object. I tried stuff like cloner.Remove() but that did not work. The setup is linked below.

                Screenshot 2020-02-18 at 19.00.21.png

                1 Reply Last reply Reply Quote 0
                • ManuelM
                  Manuel
                  last edited by Manuel

                  hello,

                  funny i've learned something (once again)

                  I told that setting the flag to HIERARCHYCLONEFLAGS_ASSPLINE will return a LineObject and the chamfer tool want SplineObject.
                  The fun fact is that the join command will transform that line object to a SplineObject.

                  So no need to use the CSTO to a temp document. Just use the Join command on the result of the GetAndCheckHierarchyClone

                  def GetVirtualObjects(self, op, hh):
                      
                          objRet = c4d.BaseObject(c4d.Onull)
                          doc = op.GetDocument()
                          if doc is None:
                              return objRet
                          objInput = op.GetDown()
                          
                          if objInput is None:
                              return objRet
                  
                          # If we set the flags to HIERARCHYCLONEFLAGS_ASSPLINE the result will be a lineObject and not a SplineObject. This will not pass the Chamfer tool.
                          hierarchyClone = op.GetAndCheckHierarchyClone(hh, objInput, c4d.HIERARCHYCLONEFLAGS_ASSPLINE , True)
                          if hierarchyClone["dirty"] is False:
                              print "return cache"
                              return hierarchyClone["clone"]
                  
                          
                          spline = hierarchyClone["clone"]
                          
                          # Use the join command so we only have one spline object. (that help in case of a hierarchy)
                          result = utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                                  list = [spline],
                                                  doc= doc)
                  
                          res = None
                          # Champher the spline
                          if result[0].IsInstanceOf(c4d.Ospline):
                              bc = c4d.BaseContainer()
                              bc[c4d.MDATA_SPLINE_CHAMFERFLAT] = True
                              bc[c4d.MDATA_SPLINE_CHAMFERRADIUS] = 19.0
                              
                              res = utils.SendModelingCommand(command = c4d.ID_MODELING_SPLINE_CHAMFER_TOOL,
                                                      list = result,
                                                      bc = bc)
                          print res
                          if res:
                              return result[0]
                          
                          return objRet
                  

                  Cheers,
                  Manuel

                  MAXON SDK Specialist

                  MAXON Registered Developer

                  1 Reply Last reply Reply Quote 0
                  • matniedobaM
                    matniedoba
                    last edited by

                    Amazing! Thank you Manuel! It works with multiple objects, Cloners etc. Without your help I would not make it 😉

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post