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

    c4d.ObjectData plugin code execution sequence

    Cinema 4D SDK
    python
    3
    8
    1.0k
    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.
    • intenditoreI
      intenditore
      last edited by intenditore

      Hi
      I'm developing an ObjectData deformer. I need to have some global variables in there, but I found some difficulty getting them right. I didn't find a clear explanation on how the plugin code is executed, so I'm struggling.

      I tried to declare variables needed in the beginning of the file:

      doc = c4d.documents.GetActiveDocument()
      fps = doc.GetFps()
      
      fC = None
      fP = None
      frameStored = 0
      if fP != None:
          velocity = 1
      else:
          velocity = 0
      

      Some are working (the first two) but when I call for frameStored from the modifier class I get:

      UnboundLocalError: local variable 'frameStored' referenced before assignment
      

      So I thought to go the other way. A dedicated class to store and calculate some global vars, it's simple:

      class storage():
          fC = None
          fP = None
          frameStored = None
          if fP != None and fC != None :
              velocity = 1
          else:
              velocity = 0
      

      I create an object of that class inside of the plugin class (I use your handy example as a base so it's called so :D)

      class SpherifyModifier(c4d.plugins.ObjectData):
          store = storage()
      

      But store var becomes undefined 0_0
      If I try "storage.frameStored" I can read and write that value! But it's wrong as there should not be an instance of the class! I didn't create it!
      At the same time when I try to create it nothing works

      But more is further - when something is calculating inside the class or in global space it does not update
      For example, "velocity" is always 0, even when fC and fP are not "None" anymore but Vector type (I checked it numerous times)

      I though the code itself is executed once when loading plugin and than Cinema calls the methods of c4d.plugins.ObjectData subclass as it needs. But it turns out the sequence is different?

      I really can't get how it works. Can somebody lead me to an explanation please?

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by m_adam

        Hi,

        @intenditore said in c4d.ObjectData plugin code execution sequence:

        doc = c4d.documents.GetActiveDocument()

        never do this in a NodeData plugin, as the the active document will not always be the document your node is in (for rendering the document is getting cloned for example). Either use the document argument passed to its methods or get it from the passed nodes (via BaseList2D.GetDocument()).

        You also seem not to be aware of the scope of your attribute declarations.

        # coding: utf-8
        class MyClass(object):
            """ A type with one attribute bound to the class and one to the instance.
            """
            my_class_attribute = "Yay, I'm bound to a class."
        
            def __init__(self):
                """
                """
                self.my_instance_attribute = "Whee, I'm bound to an instance."
        
        # What does that mean? Well, an attribute bound to a class is shared between
        # all instances of a class. In fact we even do not need an instance to
        # access it.
        print MyClass.my_class_attribute
        
        # We cannot do the same with an attribute bound to the instance of a class
        try:
            print MyClass.my_instance_attribute
        except AttributeError as e:
            print e
        
        #A class attribute is shared between all instances.
        
        a = MyClass()
        b = MyClass()
        
        print "a:", a.my_class_attribute
        print "b:", b.my_class_attribute
        
        MyClass.my_class_attribute = "whoops"
        
        print "a:", a.my_class_attribute
        print "b:", b.my_class_attribute
        
        
        Yay, I'm bound to a class.
        type object 'MyClass' has no attribute 'my_instance_attribute'
        a: Yay, I'm bound to a class.
        b: Yay, I'm bound to a class.
        a: whoops
        b: whoops
        [Finished in 0.1s]
        

        Long story short: Do not use class attributes unless you want to share the attribute between all instances.

        Cheers,
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        intenditoreI 1 Reply Last reply Reply Quote 2
        • ManuelM
          Manuel
          last edited by Manuel

          hi,

          Your object data will not be unique so unless you want to share data between instances there's no really need of using a global variable.
          Thanks @zipit again for your help 🙂

          @intenditore in your case, i would probably create a list of storage (class that you have created). Store that list in your objectdata as a class variable.

          On a side note, if you are going to store data inside your ObjectData you should implement the function CopyTo. That imply you should implement Read and Write also.

          Cheers,
          Manuel

          MAXON SDK Specialist

          MAXON Registered Developer

          1 Reply Last reply Reply Quote 0
          • intenditoreI
            intenditore @ferdinand
            last edited by

            @zipit said in c4d.ObjectData plugin code execution sequence:

            never do this in a NodeData plugin, as the the active document will not always be the document your node is in

            Useful info, thank you! So, I see there's no other way than declaring and calculating all the values in ModifyObject(), sad. It takes away some bit of organization
            I'm not really a huge fan of OOP and still a Python noob so I messed up here, yeah. You answer clarified much more in this concept than all the classes (:D) I took

            @m_magalhaes said in c4d.ObjectData plugin code execution sequence:

            Your object data will not be unique so unless you want to share data between instances there's no really need of using a global variable

            I'm slightly confused here. You mean ObjectData is recreated each frame?
            The problem I seem to stumble is that ObjectData seem to know only what's happening in one frame and no other (that's what you told if I got ir right) and each time it recreates my variables if I declare them there. Logically enough, the same happens if I declare them as global. But if I declare them before the class as global ones for the whole script, they aren't accessible and/or writable

            "UnboundLocalError: local variable 'fP' referenced before assignment"
            

            @m_magalhaes said in c4d.ObjectData plugin code execution sequence:

            i would probably create a list of storage (class that you have created). Store that list in your objectdata as a class variable

            So, to use the class as a "dummy" object to store the data as I did? The problem is the calculations inside don't work for some reason...

            ferdinandF 1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand @intenditore
              last edited by ferdinand

              @intenditore said in c4d.ObjectData plugin code execution sequence:

              @zipit said in c4d.ObjectData plugin code execution sequence:

              I'm slightly confused here. You mean ObjectData is recreated each frame?

              No an instance is created for each object.

              "UnboundLocalError: local variable 'fP' referenced before assignment"

              You should read what I have posted above. You have a crucial misconception on the scope of your attributes. But I assume you are trying to do something like this (which will raise said exception):

              class Foo(object):
                  """ This is probably what you are doing.
                  """
                  fp = 1
              
                  def bar(self):
                      """fp is not defined in this scope.
                      """
                      fp += 1
                      return fp
              
              foo = Foo()
              print foo.bar()
              """ Will raise:
              UnboundLocalError: local variable 'fp' referenced before assignment
              [Finished in 0.1s]
              """
              

              You can access the class bound attribute in your method, but this is very likely not what you want.

              class Foo(object):
                  """ This is what you could do to make a class bound attribute work.
                  """
                  fp = 1
              
                  def bar(self):
                      """ This works, but is probably not what you want, since fp is 
                      shared by all instances of Foo. So it will be incremented for all
                      instances.
                      """
                      Foo.fp += 1
                      return Foo.fp
              
              foo = Foo()
              print foo.bar()
              
              """ will print: 2
              """
              
              foo2 = Foo()
              print foo2.bar()
              
              """ will print: 3
              """
              

              This is what you probably should do.

              class Foo(object):
                  """ This is what you probably should do.
                  """
                  def __init__(self):
                      """ Define fp as an attribute bound to the instance.
                      """
                      self.fp = 1
              
                  def bar(self):
                      """
                      """
                      self.fp += 1
                      return self.fp
              
              foo = Foo()
              print foo.bar()
              
              """ will print: 2
              """
              
              foo2 = Foo()
              print foo2.bar()
              
              """ will print: 2
              """
              

              The same will apply if you encapsulate your data in your own type (which seems wildly unnecessary from what you have shown here).

              Cheers,
              zipit

              MAXON SDK Specialist
              developers.maxon.net

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

                hi,

                You define a class. For using it, you create an instance of that class.
                Data can be declared and stored in the class or in the instance.

                If the data is stored in the class, it will be the same for each instance.
                If the data is stored in the instance it will be different for each instance.

                If the data is declare in a function, it will be only available in that function.

                Here's a script that use setter and getter to change the class attributs. (cls.something) so they will change the data stored in the class
                The init function use the instance data.(self)

                In python you can have access to attribut of instance but not class. So be careful.

                # this example show how things can go wrong
                import c4d
                
                class myData (object):
                    _position = "position default"
                    _velocity = "velocity default"
                    
                    def __init__(self, position_in = None, velocity_in = None):
                        if position_in is not None:
                            self._position = position_in
                        if velocity_in is not None:
                            self._velocity = velocity_in
                    
                    @classmethod
                    def GetVelocity(cls):
                        return cls._velocity
                    
                    @classmethod
                    def SetVelocity(cls, velocity_in):
                        cls._velocity = velocity_in
                
                
                
                def main():
                    
                    
                    myInstanceA = myData()
                    myInstanceB = myData("instance B", "instance B" )
                    myinstanceC = myData()
                    
                    print "------------------------------------------"
                    print myData._position, myData._velocity ,"class"
                    print myInstanceA._position, myInstanceA._velocity, "instance A"
                    print myInstanceB._position, myInstanceB._velocity, "instance B"
                    print "------------------------------------------"
                    
                    print myInstanceA.GetVelocity(), myInstanceA._velocity
                    myInstanceA.SetVelocity("changnig velocity with setter A")
                    print myInstanceA.GetVelocity(), myInstanceA._velocity
                    print "------------------------------------------"
                    
                    print myInstanceB.GetVelocity(), myInstanceB._velocity
                    myInstanceB.SetVelocity("changnig velocity with setter B")
                    print myInstanceB.GetVelocity(), myInstanceB._velocity
                    print "------------------------------------------"
                    print myinstanceC.GetVelocity(), myinstanceC._velocity
                    myinstanceC._velocity = "changin the velocity without setter"
                    print myinstanceC.GetVelocity(), myinstanceC._velocity
                
                # Execute main()
                if __name__=='__main__':
                    main()
                

                That said, you are creating a deformer. In the Init function you can init your data for the instance. (self.mydata = "something")
                In the modify function you will be able to retrieve that data with self.mydata. change it and use it. It will be available for the next frame.

                It's a deformer, you have attached it to an object. In cinema4D, you clone that object.
                What cinema4D will do is to create a new instance of your deformer. Call the Init function and the CopyTo function. The CopyTo function (that i told you to implement) will do something like dst.myData = src.myData.
                So the copy will be the same with the same data.

                The Read and Write functions are used when you save or open a c4d file, so they must be implemented also.

                Is it more clear ?

                Cheers,
                Manuel

                MAXON SDK Specialist

                MAXON Registered Developer

                1 Reply Last reply Reply Quote 1
                • intenditoreI
                  intenditore
                  last edited by intenditore

                  Oh, mindbending. My previous main language was php and there's no such thing (you can create an analogue with static though). So I thought it works very differently. Sorry I forced you to teach me basics 😄
                  Python is painful for guys like me
                  Seems I now get how it works. Thank you all a lot!

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

                    hi,

                    without further feedback i'll set this thread as solved tomorrow.

                    Cheer,
                    Manuel

                    MAXON SDK Specialist

                    MAXON Registered Developer

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