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

    SetAllPoints() Not Working On An Object With Skin/Bind

    Cinema 4D SDK
    r21 python
    2
    9
    1.4k
    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.
    • B
      bentraje
      last edited by

      Hi,

      I have a mesh that needs a corrective pose.
      Rather than creating it within C4D, I export an uncorrected pose.
      And received a corrected pose, from the modeler.

      I now have the task to convert the corrected pose to a corrected shape.

      Here is my planned workflow:

      1. Store points of the corrected pose
      2. Go to the uncorrected pose. Add a posemorph, while in edit mode
      3. Restore points from the corrected pose to the uncorrected pose.

      It works as you can see here:
      https://www.dropbox.com/s/fkqndj892z3jlt2/c4d276_python_setallpoints_on_skin01.mp4?dl=0

      Both mesh should match 1 to 1. But not where the mesh is with Posemorph and Skin:
      https://www.dropbox.com/s/qufqggqo295atuo/c4d276_python_setallpoints_on_skin02.mp4?dl=0

      You can check the illustration file here: (Animated from 0 to 10)
      https://www.dropbox.com/s/5s9xlgujfw5ga42/c4d276_python_setallpoints_on_skin.c4d?dl=0

      Here is the working script so far:

      import c4d
      from c4d import gui
      
      # Select Corrective Pose
      # Select Base Pose
      # Execute Script
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          obj_sel = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER)
          driver = obj_sel[0]
          driven = obj_sel[1]
      
          driven_world_matrix = driven.GetMg()
          driver_world_matrix = driver.GetMg()
      
          driver_points = [point * driver_world_matrix for point in driver.GetAllPoints()]
          new_points = [point * ~driven_world_matrix for point in driver_points ]
      
          driven.SetAllPoints(new_points)
          driven.Message (c4d.MSG_UPDATE)
      
      
          print driver_points
          print new_points
      
          c4d.EventAdd()
      
      # Execute main()
      if __name__=='__main__':
          main()
      
      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi @bentraje I'm really not sure to understand correctly the workflow and your expected result.

        But just in case you seem to assume that pose morph create a kind of link to your corrected object, which is not true when you create a Relative pos, it copies the Point/Polygon information into the pose morph internal Data.
        So even in your scene if you manually modify the corrective_geo you can see that the change is not replicated into the stored corrective pose. So if you want to modify this result you have to modify this stored pose.
        To do so I let you read Pose Morph Tag which demonstrates how to do it.

        Here is your workflow more in detail if I understood correctly (but which is not really refelted by your code).

        1. You have a Cube (edited) that is rigged and skinned.
        2. You duplicate it, rename it Cube_Correct.
        3. Move Vertex 1 of Cube_Correct.
        4. Create a posemorph on Cube, and add a Relative Pose with Cube_Correct.
        5. You received a new Cube_Correct with Vertex 2 moved.
        6. Execute your script? To update the pose on the posemorph?

        So my question is does I understand your workflow right? If not feel free to explain it a bit more and tell us the whole context where your script come in action and what is the expected result on the actual scene.
        And if you have question relative to how the posemorph work, feel free to ask too, but I prefer to get the workflow right before starting a long discussion that may be useless.

        Thanks in advance,
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 1
        • B
          bentraje
          last edited by

          @m_adam

          Thanks for your response. Apologies for the confusion.

          I think in general term (or in Maya term). I need to "extract the deltas".
          Anyhow, here is the video in Maya showing the same workflow I am trying to recreate in C4D.
          https://www.dropbox.com/s/z5zi4f9tpvifr1a/c4d276_python_setallpoints_on_skin03_in_maya.mp4?dl=0

          As you can see, it perfectly fits to the corrective shape. This creates the necessary delta mesh when basemesh is at neutral pose.

          Also as you can remember in video above, in C4D demonstration without skin/pose morph, it works as expected. Only with skin/pose morph, it doesn't.

          So I tried the link you mentioned and your instruction So if you want to modify this result you have to modify this stored pose. with this revised workflow:

          1. Store points of the corrected pose
          2. Go to the uncorrected pose. Add a posemorph, while in edit mode
          3. Execute morph.SetMode(doc, morph_tag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_EXPAND, c4d.CAMORPH_MODE_REL) # to export the points
          4. Restore points from the corrected pose to the uncorrected pos
          5. Executemorph.SetMode(doc, morph_tag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_COLLAPSE, c4d.CAMORPH_MODE_AUTO)

          Unfortunately, the result is still the same as before.
          Is there a way around this?
          (See Maya video for reference and the actual output in the link above).

          P.S Regarding your workflow (7 of them). I tried reperforming them.
          The result is still the same in my first post.

          Here is the WIP code so far:

          import c4d
          from c4d import gui
          
          # Select Corrective Pose
          # Select Base Pose
          # Execute Script
          
          def main():
              doc = c4d.documents.GetActiveDocument()
              obj_sel = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER)
              driver = doc.SearchObject('corrective_geo')
              driven = doc.SearchObject('base_geo')
              morph_tag = driven.GetTag(1024237)
              morph = morph_tag.GetMorph(1)
              print morph.GetName()
          
              driven_world_matrix = driven.GetMg()
              driver_world_matrix = driver.GetMg()
          
              driver_points = [point * driver_world_matrix for point in driver.GetAllPoints()]
              new_points = [point * ~driven_world_matrix for point in driver_points ]
              
              morph.SetMode(doc, morph_tag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_EXPAND, c4d.CAMORPH_MODE_REL)
          
              driven.SetAllPoints(new_points)
              driven.Message (c4d.MSG_UPDATE)
                  
              morph.SetMode(doc, morph_tag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_COLLAPSE, c4d.CAMORPH_MODE_AUTO)
          
              c4d.EventAdd()
          
          # Execute main()
          if __name__=='__main__':
              main()
          
          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by m_adam

            Hi, I'm sorry but after re-reading for the 10thtime your topic I still don't get what you want to do.
            Making it impossible for me to help you currently.

            Can you try to explain step by step your workflow? And the action you do within Cinema 4D.
            And the most important explain the expected result within Cinema 4D.
            Maybe you can link another C4D file based on the previous one. But in the final state expected after your script execution.

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 1
            • B
              bentraje
              last edited by

              @m_adam
              RE: But after re-reading for the 10thtime your topic
              Apologies for the inconvenience.

              Here is a better illustration of the problem:
              https://www.dropbox.com/s/kuaxt8audtcafus/c4d276_python_setallpoints_on_skin04.jpg?dl=0

              Here is the code so far:

              import c4d
              from c4d import gui
              
              # Main function
              def main():
                  obj = doc.SearchObject('base_geo')
                  should_be_vector_world = c4d.Vector(3.877,2.358, -1.663)
                  should_be_vector_local = should_be_vector_world * ~obj.GetMg() # Convert to Local
                  obj.SetPoint(44, should_be_vector_local)
              
                  obj.Message (c4d.MSG_UPDATE)
                  
                  c4d.EventAdd()
              
              # Execute main()
              if __name__=='__main__':
                  main()
              

              P.S. Instead of explaining the workflow (which might be more confusing since blendshape/morph delta extraction is a bit niche), I made the question in its simplest form .

              Hope that helps.

              Let me know if you need more clarification.

              1 Reply Last reply Reply Quote 0
              • M
                m_adam
                last edited by

                Then the question is do you want this point to be at 3.877, 2.358, -1.663 all-time (so even without the morph) or only when the morph is applied?

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • B
                  bentraje
                  last edited by

                  Hi @m_adam,

                  Only when the morph is applied and to be more specific with Pose Deformers turned off.
                  It is turned off by default, but just in case.

                  1 Reply Last reply Reply Quote 0
                  • M
                    m_adam
                    last edited by m_adam

                    Hi, unfortunately, I see no way of doing what you are looking for without enabling the Post Deformers option.

                    Here a working script (you need to check the Post deformer)

                    import c4d
                    
                    def GetPointDelta(baseGeo, correctiveGeo):
                        baseGeoDeformed = baseGeo.GetDeformCache()
                        if baseGeoDeformed is None:
                            baseGeoDeformed = baseGeo.GetCache()
                            
                        if baseGeoDeformed is None:
                            raise RuntimeError("Unable to find the deformed cache")
                        
                        if baseGeoDeformed.GetPointCount() != correctiveGeo.GetPointCount():
                            raise ValueError("Corrective Pose and base Geo dont have the same point count")
                        
                        return [ptCorrective - ptBase for (ptBase, ptCorrective) in zip(baseGeoDeformed.GetAllPoints(), correctiveGeo.GetAllPoints())]
                    
                    def main():
                        # Get obj
                        baseGeo = doc.SearchObject("base_geo")
                        correctiveGeo = doc.SearchObject("corrective_geo")
                        if None in [baseGeo, correctiveGeo]:
                            raise RuntimeError("Failed to found objects")
                        
                        ptsDelta = GetPointDelta(baseGeo, correctiveGeo)
                        
                        morphTag = baseGeo.GetTag(c4d.Tposemorph)
                        if morphTag is None:
                            raise RuntimeError("Unable to find the morph tag")
                    
                        morphTag.ExitEdit(doc, True)
                    
                        # Get the base pose
                        basePose = morphTag.GetActiveMorph()
                        if basePose is None:
                            raise RuntimeError("Unable to find the base pose")
                        
                        # Expand to absolute position (absolute are still in local space of the object)
                        basePose.SetMode(doc, morphTag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_EXPAND, c4d.CAMORPH_MODE_REL)
                        
                        baseMorphNode = basePose.GetFirst()
                        for ptId in range(baseMorphNode.GetPointCount()):
                            pos = ptsDelta[ptId]
                            baseMorphNode.SetPoint(ptId, pos)
                        
                        # Collapse all again
                        basePose.SetMode(doc, morphTag, c4d.CAMORPH_MODE_FLAGS_ALL | c4d.CAMORPH_MODE_FLAGS_COLLAPSE, c4d.CAMORPH_MODE_AUTO)
                        
                        # Update the morph
                        morphTag.UpdateMorphs()
                        c4d.EventAdd()
                        print("update")
                    
                    # Execute main()
                    if __name__=='__main__':
                        main()
                    

                    But why not using a Correctional PSD morph this is exactly the purpose of them. Providing correctional pose given some Joint Orientation (be sure to be in the mode "in place" before calling SetAllPoint).

                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    1 Reply Last reply Reply Quote 1
                    • B
                      bentraje
                      last edited by

                      @m_adam

                      Thanks for the response.

                      RE: without enabling the Post Deformers option.
                      It seems like so. I initially thought I could reverse engineer it by getting the point's skin influence (its joint and its weight) and in a way factor it with the intended world vector. However, I gave up since I realize point can have several joints and several weight. Haha

                      Don't get me wrong, enabling the post deformer works as expected.
                      The problem comes because of its inherent nature
                      "calculate the points position of the morph after they are deformed by a Deformer."

                      It presupposes that the (near) last deformation comes from the pose morph.
                      For example, Squash/Stretch>Bulge>Bend>Twist>Pose Morph.
                      However, there are cases where the intended deformation is
                      Pose Morph>Squash/Stretch>Bulge>Bend>Twist.

                      So having the post deformer off while still getting the world position desired gives a bit of flexibility.

                      Anyhow, thanks again for the help.
                      I'll just use the post deformer for now and see where I get stuck.

                      Have a great day ahead!

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