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

    Make editable an object generated by GetVirtualObjects()

    Cinema 4D SDK
    python
    3
    19
    2.6k
    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.
    • ferdinandF
      ferdinand
      last edited by ferdinand

      Hi,

      yes, while I still have no c4d here (so I could not use your zip), this does make things a lot more understandable for me.

      Here is a quick sketch on how I would go about implementing, what you want to do.

      class LightRig(c4d.plugins.ObjectData):
          
          def __init__(self):
              '''
              '''
              self._light_rig_cache = {}
          
          def Init(self, op):
              '''
              '''
              self.SetOptimizeCache(True)
              return True
              
          def _load_first_object_from_file(self, path):
              ''' Returns the first object from a file path interpreted as a c4d
               document.
                  
                  Args:
                      path (str): The file path.
                  
                  Raises:
                      IOError: When the file path does not exists or could not be red.
                      IOError: When the document does not contain any objects.
                  
                  Returns:
                      c4d.BaseObject: The first object of the document.
              '''
              doc = c4d.documents.LoadDocument(path, c4d.SCENEFILTER_NONE, None)
              
              if doc is None:
                  msg = 'Could not load file.'
                  raise IOError(msg)
              
              op = doc.GetFirstObject()
              if op is None:
                  msg = 'Target object not found.'
                  raise IOError(msg)
                  
              return op.GetClone()
              
          def _get_light_rig(self, lid):
              ''' Either loads the light rig with the given id from a file or
               returns a copy of the local cache.
                  
                  Args:
                      lid (int): The integer identifier for the requested light rig.
                      
                  Raises:
                      IOError: When the hardcoded file path does not exists or could
                       not be red.
                      ValueError: If lid is not a legal light rig id.
                  
                  Returns:
                      c4d.BaseObject: The requested light rig.
              '''
              # Return an cached object from our ligth rig hash table
              if lid in self._light_rig_cache:
                  return self._light_rig_cache[lid].GetClone()
              
              # Or load it from a file and add it to the cache
              if lid == c4d.ID_MY_FANCY_LIGHT_RIG_1:
                  rig = self._load_first_object_from_file(SOME_FRIGGING_FILE_PATH_1)
                  self._light_rig_cache[lid] = rig
              elif lid == c4d.ID_MY_FANCY_LIGHT_RIG_2:
                  rig = self._load_first_object_from_file(SOME_FRIGGING_FILE_PATH_2)
                  self._light_rig_cache[lid] = rig
              # ...
              else:
                  msg = 'Illegal light rig id: {}.'
                  raise ValueError(msg.format(lid))
              return rig.GetClone()
                  
          def GetVirtualObjects(self, op, hierarchyhelp):
              ''' Returns the preloaded light rig cache.
              '''
              # The more verbose form would be somehing like:
              # if op[c4d.LIGTH_RIG_TYPE] == c4d.ID_MY_FANCY_LIGHT_RIG_1:
              #     return self.get_light_rig(c4d.ID_MY_FANCY_LIGHT_RIG_1)
              # elif ...
              return self._get_light_rig(op[c4d.LIGHT_RIG_TYPE])
      

      A few notes:

      1. I wrote this on an iPad from my head, so, jeah, view it as a 'draft'.

      2. Using predefined data from a document is still a rather exotic concept. You might have to preload all data, when you run into threading issues.

      3. The preloading / caching of your light rigs is in general a problem, as it will increase the memory footprint of your plugin.

      4. Building your rigs procedurally might be a better option performancewise.

      Cheers
      zipit

      MAXON SDK Specialist
      developers.maxon.net

      mfersaouiM 1 Reply Last reply Reply Quote 0
      • mfersaouiM
        mfersaoui @ferdinand
        last edited by mfersaoui

        @zipit
        Hi,
        Here is short video capture to compare between the result of my method and the result of your method, in my method the object stay visible when I moving the Handle point or make change in object data instance but with your method the object is not visible when I moving the Handle point or make change in object data instance.
        For this reason I'm using this method.

        Thank you for your time.

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

          Hi,

          1. I am absolutely fine with it if you prefer something else.
          2. I do not know what you are doing exactly in your video, but yes, "your" side does look better. But that almost has certainly nothing todo how you impement your GVO.
          3. I have to point out again that there is no my method and your method. You are using GVO in a way that is not allowed by the c4d SDK. Read the provided links above on threading. They are crystal clear on that topic. Or contact the the SDK support team, they will tell you the same.
          4. While things might work in your small test environment, they won't in a production environment. Consider how pissed your clients or your company will be when your plugin has corrupted their files.

          Consider yourself to have been warned 😉

          Cheers
          zipit

          MAXON SDK Specialist
          developers.maxon.net

          mfersaouiM 1 Reply Last reply Reply Quote 0
          • mfersaouiM
            mfersaoui @ferdinand
            last edited by mfersaoui

            @zipit
            Hi,
            Sorry, I said "My method" and "Your method" just to differentiate between the two methods. I don't said that my method is better I'm just searching a legal method that allow me to obtain a similar result as the method that I'm using now.
            in the method that I'm using the preset is loaded in document only one time. So, for example when I move the object handle points the change is applied into the previous loaded object.

            The loaded object has a python tag that contain all object controls.
            here is an example of python tag content:

            import c4d
            
            def main():
                obj = op.GetObject()
                parent = obj.GetUp()
                scale = parent[c4d.PRIMITIVEOBJECTS_GLOBAL_SCALE]
                
                # Objects scale
                obj.SetAbsScale(c4d.Vector(scale))
            

            But with second method at each change the preset object is reloaded from disc or cache with the new changes (the preset object is removed then reloaded with the new changes) and that result that the object is becomes not visible in the View during a few thousandths of a second.

            I'm thinking about another alternative, is the following code cause the same problem:

            def GetVirtualObjects(self, op, hierarchyhelp):
                    doc = documents.GetActiveDocument()
            
                    path = os.path.join(os.path.dirname(__file__), "res", "my_scene.c4d")
                    load = c4d.documents.MergeDocument(doc, path, c4d.SCENEFILTER_OBJECTS)
                    if load is False:
                        return True
                    
                    return c4d.BaseObject(c4d.Onull)
            
            1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand
              last edited by ferdinand

              Hi,

              no need to be sorry, I am / was not offended. I was just trying to be very clear about the implications of your approach.

              1. If you want to have a statically generated object, ObjectData is not the correct base class for you, as its whole purpose is to provide a dynamically generated object when you overwrite GVO.
              2. Loading an object from a preset file is quite an unusal thing to do for the ObjectData type. The more common thing to do, would be to construct your rig procedurally within your GVO. Which would probably also (apart from performance benefits) alleviate the problems you have shown in the video.
              3. Also noteworthy is, that a python scripting tag also calls the draw() function of your script, which you could use to draw / react to to handles with your original xpresso and user data steup.

              Cheers
              zipit

              MAXON SDK Specialist
              developers.maxon.net

              mfersaouiM 1 Reply Last reply Reply Quote 1
              • mfersaouiM
                mfersaoui @ferdinand
                last edited by mfersaoui

                @zipit
                Hi,
                Thank you, I'm trying to rebuild all my rig inside the GVO and I think I will using C++.
                is it better to use c++?

                Cheers
                Mustapha

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

                  Hi,

                  @mfersaoui said:

                  is it better to use c++?

                  I think the answer to that is highly subjective. Both SDKs and languages have their strengths. As your plugin probably is neither going to be computationally expensive, nor will be in need of any of the more advanced features only the C++ SDK does offer, I would say, Python wins this cost/gain comparison by a long shot.

                  But that is subjective 😉

                  Cheers
                  zipit

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • S
                    s_bach
                    last edited by s_bach

                    Hello,

                    just to make some things clear:

                    • The purpose of GetVirtualObjects() is to fill a cache with virtual objects.
                    • It is NOT the purpose of GetVirtualObjects() to modify the document / the scene graph.
                    • It is forbidden to edit the scene graph from within the scene execution pipeline e.g. from within GetVirtualObjects().
                    • One must only edit the scene graph in reaction to some user interaction e.g. after the user pressed some button in the UI or used a tool.
                    • One must never call GetActiveDocument() in a NodeData based function. The active document is the document currently displayed in the viewport; but there is no reason to think that this is the document that hosts the object.
                    • Consequentially, one must never call EventAdd() in a NodeData based function; especially not from within the execution pipeline.
                    • If you want your generator to load a given scene file, you can load that scene with LoadDocument() and just COPY the content into the cache.
                    • The Python API is an incomplete wrapper around the C++ API. So some functions may be missing in the Python API.

                    best wishes,
                    Sebastian

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    mfersaouiM 2 Replies Last reply Reply Quote 1
                    • mfersaouiM
                      mfersaoui @s_bach
                      last edited by

                      @s_bach said in Make editable an object generated by GetVirtualObjects():

                      GetVirtualObjects()

                      Hello,
                      Thank you, I decided to use C++ to do that, I just post new topic "MAXPORTS GraphView Flags - Description Resource" is a continuity of this topic.

                      1 Reply Last reply Reply Quote 0
                      • mfersaouiM
                        mfersaoui @s_bach
                        last edited by

                        @s_bach said in Make editable an object generated by GetVirtualObjects():

                        LoadDocument()

                        Hello,
                        I gave up the idea to merge objects from scene file, I had generate my object and all her sub-objects inside the GetVirtualObjects(). But now I'm searching how to assign materials to my sub-objects, I would imagine that is the same as objects, should I use the LoadDocument()? if yes are there examples? and then can I change these materials proprieties from the object description resource or I must to use CommandData dialog?
                        Thank you.

                        1 Reply Last reply Reply Quote 0
                        • S
                          s_bach
                          last edited by

                          Hello,

                          a generator (GetVirtualObjects()) cannot create materials. You must generate materials in reaction to user-interaction (Material Manual). Then you can assign materials to your (virtual) objects using a TextureTag (TextureTag Manual).

                          for example, you could detect when your generator object is created from the UI by listening to the MSG_MENUPREPARE message in NodeData.Message(). Then you can create all the materials you want to use later.

                          best wishes,
                          Sebastian

                          MAXON SDK Specialist

                          Development Blog, MAXON Registered Developer

                          mfersaouiM 1 Reply Last reply Reply Quote 2
                          • mfersaouiM
                            mfersaoui @s_bach
                            last edited by mfersaoui

                            @s_bach
                            Hello,
                            Thank you, As you explained, I create all materials in NodeData.Message() and then I called them in the GVO. I just want to know if is it allowed to edit the called material parameters inside the GVO or not? see example bellow:

                            BaseObject* MyObject::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) {
                                    ...
                                    BaseObject* container = BaseObject::Alloc(Onull);
                            	if (!container)
                            		return BaseObject::Alloc(Onull);
                            
                            	container->SetName("Container");
                            
                            	BaseMaterial* material = doc->SearchMaterial("my_material");
                            	if (material) {
                            		// create the texture tag
                            		TextureTag* const textureTag = static_cast<TextureTag*>(container->MakeTag(Ttexture));
                            		if (textureTag == nullptr)
                            			return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
                            
                            		const Float Brightness = data->GetFloat(MYOBJECT_LIGHT_INTENSITY);
                            		
                            		// Is the following action allowed inside GVO:
                            		material->SetParameter(DescID(MATERIAL_LUMINANCE_BRIGHTNESS), Brightness, DESCFLAGS_SET_0);
                            
                            		textureTag->SetMaterial(material);
                            	}
                                    ...
                            }
                            

                            Best,
                            Mustapha

                            mfersaouiM 1 Reply Last reply Reply Quote 0
                            • mfersaouiM
                              mfersaoui @mfersaoui
                              last edited by mfersaoui

                              I think this is not allowed. I've just seen the list of the Forbidden Functions and "Make any changes to materials." is inside.

                              1 Reply Last reply Reply Quote 0
                              • S
                                s_bach
                                last edited by

                                Hello,

                                as said before, it is not the purpose of GetVirtualObject() to edit the scene or to edit elements of the scene; this includes materials.

                                MAXON SDK Specialist

                                Development Blog, MAXON Registered Developer

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