Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Right way to get object out?

    Scheduled Pinned Locked Moved PYTHON Development
    18 Posts 0 Posters 1.8k Views
    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.
    • H Offline
      Helper
      last edited by

      On 02/04/2017 at 10:15, xxxxxxxx wrote:

      I encountered very strange problem while copying matrix (the same happens when I simply unparent in gui)
      The main purpose of the script I tried to write was an ability to unparent animated object without weird jump, when the animated properties are not (how to say?) "compensated" the way static are. So animated properties are counted from new parent, but with old offsets, and the absolute values are totally different from what it was before.
      Only recently I noted there already is such function built in C4D, but, anyway, I decided to finish the work.
      What's on the video. The child object has animation on one position and one rotation track. When I unparent it two strange things happen: rotation on animated axis changes only by 10 degrees (??) and being free from the parent it starts to move in completely opposite direction! WTF is happening?? I had lost any understanding how to compensate it all to get the object back where it was before unparenting
      I would highly appreciate if you can explain how to deal with that

      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        On 02/04/2017 at 10:53, xxxxxxxx wrote:

        Originally posted by xxxxxxxx

        Why do you remove it before inserting?
        Since without it's working well.

        Try that in C++ and see what happens. It will be painful. !Smile[URL-REMOVED]
        Python is allowing you to doing some major memory handling cheating here.
        IMHO. You should still use Remove() in python even if it seems to work without it.

        -ScottA


        [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

        1 Reply Last reply Reply Quote 0
        • H Offline
          Helper
          last edited by

          On 03/04/2017 at 11:10, xxxxxxxx wrote:

          Hi,

          in general a any GeListNode can only be part of one single list at a time. So you need to Remove() it before inserting it into a new list (or another position of the same).

          Unfortunately I can't watch your video (seems to be marked private). So I'm not quite sure, what the actual problem is.
          In order to work with animated objects, you will certainly want to take a look at CTrack and CKey classes.

          1 Reply Last reply Reply Quote 0
          • H Offline
            Helper
            last edited by

            On 03/04/2017 at 11:11, xxxxxxxx wrote:

            Ooopsy, I forgot to welcome you in the Plugin Café forums, Intenditore. Welcome! 🙂

            1 Reply Last reply Reply Quote 0
            • H Offline
              Helper
              last edited by

              On 03/04/2017 at 12:44, xxxxxxxx wrote:

              Originally posted by xxxxxxxx

              Ooopsy, I forgot to welcome you in the Plugin Café forums, Intenditore. Welcome! 🙂

              Thank you! 🙂 Glad to join your community)

              I edited settings, now the video is viewable

              1 Reply Last reply Reply Quote 0
              • H Offline
                Helper
                last edited by

                On 04/04/2017 at 08:18, xxxxxxxx wrote:

                I stumbled upon a great misconception in my head 😄
                How do axis work?
                As I imagine, if we have a parent object and it's child, so the child object's PSR might be calculated like this:
                position would equal absolute (world coordinate) parent position + relative (to parent) child position (seems easy);
                scale - parent scale multiplied by child scale (but! What if the child is rotated inside, so the parent is scaled up on x axis, for instance, but in projection to child it will be y axis, for example? Or both Z and Y if it's angled? how to deal with that?);
                and than rotation - firstly I thought it's pretty easy, we simply must take parent H and summ it with child H (and so the same with B and P), but it turned out to be much more, extensively complicated!
                First - do math operations order matter? In what order do we need to compute new coordinates, what axis is primary and needs to be set first? Or there's no difference (I doubt it could be so)
                Second - what if parent and child have their axis misaligned or flipped or even angled? How to get the angles we need to add/substract to get to original values after unparenting?
                And that's not only scripting-related question, I know many (including me) are curious seeing such different values as on the screenshot

                I will be very grateful if you can explain me the concept behind that magic :D. I think it's very important and necessary for many more people not only me

                1 Reply Last reply Reply Quote 0
                • H Offline
                  Helper
                  last edited by

                  On 04/04/2017 at 08:43, xxxxxxxx wrote:

                  Basicly op.GetMg(the global matrix) is egal to Parent GlobalMatrix + Current obj local Matrix.
                  Then you can do this thing recursivly.
                  Moreover keep in mind HPB is a Vector not a Matrix. And that is very important cause Matrice is 4 Vector(3 for rotation) while HBP is an unique Vector whro represente these 3Vectors. So a conversion is needed. And in all conversion you have to keep in mind you can loose some informations. It's why you can have for exemple 0 or 360 in H. Because from the conversion and from a logical point of view it's the same value while in animation it can be a problem.
                  Moreover take a look about utils who talk about HPB and axis order 😉
                  function. https://developers.maxon.net/docs/py/2023_2/modules/c4d.utils/index.html?highlight=hpb#c4d.utils.MatrixToHPB

                  A good read about that https://developers.maxon.net/docs/py/2023_2/misc/matrixfundamental.htm

                  So in your case your master null (An) have probably already a rotation. since in the object panel you are into the object local position while in the coordinate manager you are in world coordinate.
                  And that why thoses values are differents.

                  Hope it's help you more than confuse you.

                  1 Reply Last reply Reply Quote 0
                  • H Offline
                    Helper
                    last edited by

                    On 04/04/2017 at 09:06, xxxxxxxx wrote:

                    There is a pretty good article on matrices in the COFFEE docs.
                    But for what you're trying to do. That information might just confuse you more (although it's good to know that stuff).

                    The problem you're facing is that the values for the animation keys were all set using a specific parent. So when you change to a different parent object (in this case the world). Those key values are wrong.
                    You really just need to compensate for the difference between the old parent and the new parent. Then use that difference value to update every single animation key in the child object.
                    The down side to this is that editing animation keys takes a lot of code. And it can get quite messy. I've been trying to think of a trick to make it simpler. But I haven't been able to think of one yet.

                    Here is an example.
                    Note that this does not handle scaling or any other types of animation tracks. Those tracks would need to be added as desired.

                    #Suppose you have an object with a child object called "Cube.1"  
                    #And you want to move the child out from under the parent so it's a child of the world  
                    #If the child has no keys on it. You can simply copy the child's global matrix then apply that matrix to it after it's moved in the OM   
                    #But if the child has animation keys on it. You'll need to convert the values for each key to respect the world as the parent object  
                    #In other words. The animation key values need to be converted with a calculation  
                      
                    import c4d  
                    def main() :      
                      
                      child = doc.SearchObject("Cube.1") #<---Or whatever your object's name is  
                      if child is None: return  
                      cMtx = child.GetMg()  
                        
                      parent = child.GetUp()  
                      if parent is None: return  
                      pMtx = pMtx = parent.GetMg()  
                        
                      worldMtx = c4d.Matrix()      
                      vec = pMtx.off - worldMtx.off  
                        
                      #Get all the position key values on the child object  
                      #Offset them by the distance the parent object is from the world matrix  
                      #Store those values in list arrays  
                      posX = []  
                      posY = []  
                      posZ = []  
                      rotX = []  
                      rotY = []  
                      rotZ = []  
                        
                      doc.StartUndo()  
                      doc.AddUndo(c4d.UNDOTYPE_CHANGE, child)  
                        
                      tracks = child.GetCTracks()  
                        
                      ################ Positions ##################   
                        
                      for track in xrange(len(tracks)) :                 
                          if tracks[track].GetName() == "Position . X":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  posX.append(key.GetValue() + vec.x)                   
                                    
                          if tracks[track].GetName() == "Position . Y":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  posY.append(key.GetValue() + vec.y)   
                                                   
                          if tracks[track].GetName() == "Position . Z":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  posZ.append(key.GetValue() + vec.z)  
                                    
                          ################ Rotations ##################          
                                    
                          if tracks[track].GetName() == "Rotation . X":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  rotX.append(key.GetValue() + vec.x)                   
                                    
                          if tracks[track].GetName() == "Rotation . Y":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  rotY.append(key.GetValue() + vec.y)   
                                                   
                          if tracks[track].GetName() == "Rotation . Z":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  rotZ.append(key.GetValue() + vec.z)     
                                    
                                    
                        
                      #Change the child's parent to the world by moving it in the OM      
                      child.Remove()  
                      doc.InsertObject(child,None,parent)  
                      child.Message(c4d.MSG_UPDATE)  
                            
                      #Now we need to change each of the child object's keys  
                      #Replacing them with the values stored in the lists  
                      for track in xrange(len(tracks)) :                 
                          if tracks[track].GetName() == "Position . X":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)                  
                                  key.SetValue(curve, posX[k])    
                                    
                          if tracks[track].GetName() == "Position . Y":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  key.SetValue(curve, posY[k])  
                                                   
                          if tracks[track].GetName() == "Position . Z":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  key.SetValue(curve, posZ[k])  
                                    
                                    
                          if tracks[track].GetName() == "Rotation . X":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)                  
                                  key.SetValue(curve, rotX[k])    
                                    
                          if tracks[track].GetName() == "Rotation . Y":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  key.SetValue(curve, rotY[k])  
                                                   
                          if tracks[track].GetName() == "Rotation . Z":   
                              curve = tracks[track].GetCurve()  
                              for k in xrange(curve.GetKeyCount()) :  
                                  key = curve.GetKey(k)   
                                  key.SetValue(curve, rotZ[k])                  
                                    
                      child.Message(c4d.MSG_UPDATE)                  
                      doc.EndUndo()  
                        
                      c4d.DrawViews(c4d.DRAWFLAGS_FORCEFULLREDRAW)  
                      c4d.EventAdd()  
                      
                    if __name__=='__main__':  
                      main()
                    

                    -ScottA

                    1 Reply Last reply Reply Quote 0
                    • H Offline
                      Helper
                      last edited by

                      On 04/04/2017 at 12:31, xxxxxxxx wrote:

                      Oh wow 😵 What an incredibly detailed answers! Huge!
                      Basically the concept you describe is exactly the same as the one I had, but something does not work yet.
                      gr4ph0s, it could be much easier, but as I have to deal with individual keys it all gets much more complicated. I decided that getting initial values without matrices is more straight-forward but it looks basically the same as in the variant you've described, on principal level.
                      I have red the article about Matrix before but thanks anyway, it seems I got it ))
                      ScottA, wow! Thank you for that huge piece of work)
                      I have written about the same thing, but I call tracks not in loop but strictly by their names because I noticed in Cinema you can add second or even third curve on the same property, so there may be a few curves you will get this way as I got it (still I completely can't understand what it could be used for and how to utilize it)
                      Anyway, that's my code
                      This is partially working snippet, still unfinished. It works but has a very strange problem. I see the content of staticRotation_parent is not corresponding to the curves_rotation. Both must contain X, Y and Z in that exact order, but in staticRotation_parent the order is strangly different as I discovered (not sure though, maybe the curves_rotation is wrong, but there's much less chances it is).

                        
                        
                      import c4d  
                      from c4d import gui  
                      import math  
                      #Welcome to the world of Python  
                        
                      obj = doc.GetActiveObject()  
                        
                      inicialMg = obj.GetMg()  
                        
                      #GET TRACKS  
                        
                      tracks_position = [  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
                      ]  
                      tracks_scale = [  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
                      ]  
                      tracks_rotation = [  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
                        obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
                      ]  
                        
                      #RETRIVE CURVES  
                        
                      curves_position = []  
                      curves_scale = []  
                      curves_rotation = []  
                        
                      for i in tracks_position:  
                        if i:  
                            curves_position.append(i.GetCurve())  
                        else:  
                            curves_position.append(None)  
                        
                      for i in tracks_scale:  
                        if i:  
                            curves_scale.append(i.GetCurve())  
                        else:  
                            curves_scale.append(None)  
                        
                      for i in tracks_rotation:  
                        if i:  
                            curves_rotation.append(i.GetCurve())  
                        else:  
                            curves_rotation.append(None)  
                        
                      #GET ABS VALS  
                        
                      parentObj = obj.GetUp()  
                        
                      if parentObj:  
                        staticPosition_parent = parentObj.GetAbsPos()  
                        staticScale_parent = parentObj.GetAbsScale()  
                        staticRotation_parent = parentObj.GetAbsRot()  
                        
                        
                      nParent = parentObj.GetUp()  
                        
                      #UNDO  
                      doc.StartUndo()  
                        
                      #MOVE TARGET OBJECT FROM PARENT  
                        
                      if (nParent) :  
                        doc.AddUndo(c4d.UNDOTYPE_DELETE, obj)  
                        obj.Remove()  
                        doc.AddUndo(c4d.UNDOTYPE_NEW, obj)  
                        obj.InsertUnder(nParent)  
                      else:  
                        doc.AddUndo(c4d.UNDOTYPE_DELETE, obj)  
                        obj.Remove()  
                        doc.AddUndo(c4d.UNDOTYPE_NEW, obj)  
                        doc.InsertObject(obj)  
                        
                      #UNDO  
                      doc.EndUndo()  
                      c4d.EventAdd()  
                        
                      #RESET GLOBAL MATRIX  
                        
                      obj.SetMg(inicialMg)  
                        
                      #COMPENSATE KEY VALUES  
                        
                      q = 0  
                      for i in curves_rotation:  #ITERATE CURVES ARRAY  
                        if i:    #ITERATE KEYS OF CURVE  
                            q2 = 0  
                            for l in range(i.GetKeyCount()) :  
                                key = i.GetKey(q2)  
                                value = key.GetValue() + staticRotation_parent[q]  
                                key.SetValue(i, value)  
                                q2 += 1  
                        q += 1  
                        
                      # q = 0  
                      # for i in curves_position:  #ITERATE CURVES ARRAY  
                      #     if i:    #ITERATE KEYS OF CURVE  
                      #         q2 = 0  
                      #         for l in range(i.GetKeyCount()) :  
                      #             key = i.GetKey(q2)  
                      #             key.SetValue(i, keysModified_position[q][q2])  
                      #             print(key.GetValue())  
                      #             q2 += 1  
                      #     q += 1  
                        
                      c4d.EventAdd()  
                        
                      
                      1 Reply Last reply Reply Quote 0
                      • H Offline
                        Helper
                        last edited by

                        On 04/04/2017 at 12:57, xxxxxxxx wrote:

                        I wish I had a simpler answer for you. But when you target animation tracks the code gets messy looking and complex. And it's easy to write code that works for some case and not others.
                        The only way to get it right is by trial and error.

                        Lists are very slow in Python. And if you have hundreds or thousands of keys. This code is going to be dog slow.
                        I still think there might possibly be a better, cleaner way to change the keys.
                        But I couldn't think of anything.

                        -ScottA

                        1 Reply Last reply Reply Quote 0
                        • H Offline
                          Helper
                          last edited by

                          On 05/04/2017 at 10:15, xxxxxxxx wrote:

                          Just want to add, that the correct way is to work with transformation matrices instead of adding or multiplying for example rotation vectors.
                          Otherwise I hope you guys are progressing nicely, as we are currently lacking the time to review these amounts of code.

                          1 Reply Last reply Reply Quote 0
                          • H Offline
                            Helper
                            last edited by

                            On 05/04/2017 at 17:48, xxxxxxxx wrote:

                            OMG, I just saw your procedural modeling tools on Labs and now you answer to my thread! %)
                            I now investigate what's wrong with my code. It can take a long time as I have main work, but I will post updates when I go further so somebody could follow my steps if needed
                            By the way, thanks for amazing software! And very friendly forum)

                            1 Reply Last reply Reply Quote 0
                            • H Offline
                              Helper
                              last edited by

                              On 06/04/2017 at 02:18, xxxxxxxx wrote:

                              I guess, it's no surprise that we (MAXON's SDK Team) do some development work as well, after all we also need some practicing with our SDKs. And now that we have MAXON Labs, we also have a way to make our side projects public.

                              But in case of the Py-ParametericTools main kudos go to Thanassis/Noseman, who had the idea and needed it for a project of his. And if you take a look at the code, I'm sure you will see, there's not much to it, basically just calling SendModelingCommand().

                              One more thing on this topic:
                              Before anybody says "the SDK Team is wasting time into MAXON Labs instead of providing us with new examples". Rest assured, we are not. Py-ParametericTools are available in source (not encrypted) directly on MAXON Labs. And Riccardo is working heavily on new examples as well. Sometimes it's just a bit easier to release a binary, than an example source. The later simply needs some more time to be invested into cleanup, commenting and documentation. So, no work is done exclusively for MAXON Labs, quite the opposite, we just use MAXON Labs to make some of our SDK work available to a broader audience. I hope, our community doesn't mind.

                              1 Reply Last reply Reply Quote 0
                              • H Offline
                                Helper
                                last edited by

                                On 06/04/2017 at 02:29, xxxxxxxx wrote:

                                Don't care about what you should do.
                                Every one here know the real job you do and it's pretty amazing !

                                I also developped mine parametric tool for spline (offset and chamfer) it's very usefull. And for understand caching system.
                                I was wondering if we can help the maxon labs by anyway or maybe for the github exemple.

                                EDIT: Sorry for this off topic Intenditore.

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