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

    FieldList, and how to properly use it

    Cinema 4D SDK
    r20 r21 s22 r23 c++
    2
    8
    945
    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.
    • fwilleke80F
      fwilleke80
      last edited by fwilleke80

      Hello,

      in my plugin, I support Fields. I have a FIELDLIST element in my .res file, and I can get the FieldList custom datatype, et cetera.

      But there are some things I really don't know how to handle:

      1. Dirtyness
      I need to track dirtyness of all Field objects in the list, so I can determine when to rebuild my plugin object's cache. FieldList::GetDirty() seems to catch only changes that were made to the list, not to any of the stuff in the list. I found that the IsDirty() functions often report dirtyness when actually nothing has changed, so I am using the GetDirty() function of each Field object, then store the dirty checksums and later compare if the checksums I get from the Field objects to see if they have changed.

      This seems to work ok. However, it's quit a lot of code required, just for tracking the dirtyness:

      // Check for dirty fields
      GeData geFieldData;
      if (op->GetParameter(MYOBJECT_FIELDS_LIST, geFieldData, DESCFLAGS_GET::NONE))
      {
      	// Get FieldList
      	CustomDataType* const fieldData = geFieldData.GetCustomDataType(CUSTOMDATATYPE_FIELDLIST);
      	FieldList* const fieldList  = static_cast<FieldList*>(fieldData);
      	if (fieldList && fieldList->HasContent())
      	{
      		// Dirty fieldList
      		_currentDirtyChecksum_fieldList = fieldList->GetDirty(doc);
      		needsEvaluation |= _currentDirtyChecksum_fieldList != _compareDirtyChecksum_fieldList;
      
      		// Dirty field objects
      		GeListHead *listHead = fieldList->GetLayersRoot();
      		if (listHead)
      		{
      			_currentDirtyChecksum_fieldLayers = 0;
      			_currentDirtyChecksum_fieldObjects = 0;
      			for (FieldLayer *layer = static_cast<FieldLayer*>(listHead->GetFirst()); layer; layer = IterateNextFieldLayer(layer))
      			{
      				// Layer node (in list)
      				_currentDirtyChecksum_fieldLayers += layer->GetDirty(DIRTYFLAGS::DATA);
      
      				GePrint(layer->GetName());
      
      				// Actual field object
      				const FieldLayerLink layerLink = layer->GetLinkedObject(doc);
      				BaseObject *fieldObject = static_cast<BaseObject*>(layerLink._object);
      				if (fieldObject)
      				{
      					_currentDirtyChecksum_fieldObjects += fieldObject->GetDirty(DIRTYFLAGS::CACHE|DIRTYFLAGS::DATA|DIRTYFLAGS::MATRIX);
      				}
      			}
      			needsEvaluation |= _currentDirtyChecksum_fieldLayers != _compareDirtyChecksum_fieldLayers;
      			needsEvaluation |= _currentDirtyChecksum_fieldObjects != _compareDirtyChecksum_fieldObjects;
      		}
      	}
      }
      

      _currentDirtyChecksum_fieldObjects, _currentDirtyChecksum_fieldLayers, _compareDirtyChecksum_fieldLayers, and _compareDirtyChecksum_fieldObjectsare members of my NodeData. The _compare*checksums are later updated with the values from the _current*checksums.

      2. FieldList contents
      There are the functions FieldList::HasContent() and FieldList::GetCount(). Why is this:

      • Removing Field objects from the FieldList correctly decreases the return value of GetCount(), and, after removing the last Field object from the list, makes HasContent()return false.
      • Deleting Field objects that are linked in the FieldList from the document does not decrease the the return value of GetCount() and HasContent() always returns true.

      That means, if the user deletes linked Field objects in the Object Manager, my plugin object is permanently broken. It behaves as if there still were Field objects to take into account. And the user has no chance of repairing it, because the FieldList appears empty to the user.

      I know this behaviour is consistent with the InExcludeList, which also maintains zombie BaseLinks. But I never understood the use of this. I also tried iterating the list and removing all zombie elements by deleting the list item if GetLinkedObject() does not return a valid pointer, but unfortunately that would also remove folders in the FieldList.

      MoGraph seems to handle it well. If I delete a Field object that is linked in an effector object, the effector will show full effect, even though there should be a zombie link in the FieldList. What does MoGraph do to handle this so robustly?

      It really is a pity that there's not a single example in the SDK (not in the GitHub repo either) that uses a FieldList.

      Thanks for any help!

      Cheers,
      Frank

      www.frankwilleke.de
      Only asking personal code questions here.

      1 Reply Last reply Reply Quote 0
      • fwilleke80F
        fwilleke80
        last edited by

        Is anyone from the Tech Support looking into this?
        Or is any of this unclear, should I elaborate more?

        www.frankwilleke.de
        Only asking personal code questions here.

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

          Hi @fwilleke80 sorry I was busy yesterday, so for the first point there is nothing to help you more, as you may know, some mograph stuff (especially if you rely on matrix or shader) are constantly dirty... And if one of those is on the fieldlist, then all the fieldlist will be dirty. So your approach to monitoring each one is the most optimized.

          Regarding your second point, I've reached the development team. For me, the reason is for undo operation, but that's true its a bit unexpected, so let's see what the development team will say.
          But when a field object is deleted, the FieldLayer has his flags set to SKIP | HIDE | TEMPORARY. You can retrieve them via FieldLayer::GetLayerFlags.
          To Exclude folder you can simply check FieldLayer for its type FLfolder.

          Regarding example we do have mograph_fields.cpp which demonstrate:

          • A CommandData that samples a FieldObject
          • A CommandData that samples the field list of a plain effector.
          • A Custom FieldData implementation
          • A FieldLayerData implementation

          So you are right we don't have an example of how to handle field within an ObjectData. This is something we will add on our ToDo list.

          Cheers,
          Maxime.

          MAXON SDK Specialist

          Development Blog, MAXON Registered Developer

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

            Just et a reply from the developer and as suspected this is to properly support undo.

            Hi,

            This is the same system used in inexclude lists throughout Cinema. The object in the list isnโ€™t the object itself, just a layer with a link to the object, if the real world object is deleted then the entry simply points to nothing. When you delete in the list though you are deleting the layers and adding undos to the object the list is on via the GUI system, the list additionally checks for any other uses of the object in scene and deletes it as well if none are found.

            Because of differences in how the object system and GUI system works, and that fields may be shared in multiple lists this approach allows undos, GUI display and evaluation to work correctly. These layers are then marked for omission in file operations.

            Hope it makes sense,
            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • fwilleke80F
              fwilleke80
              last edited by fwilleke80

              Thank you! I'll see if the flags solve my problem. Sounds like I can work with this.
              I'll mark this SOLVED for now!

              An example for a FieldList in an ObjectData would be most appreciated! ๐Ÿ™‚

              Cheers,
              Frank

              www.frankwilleke.de
              Only asking personal code questions here.

              1 Reply Last reply Reply Quote 0
              • fwilleke80F
                fwilleke80
                last edited by fwilleke80

                Back from a short autumn vacation. Sorry, I have to mark this as UNSOLVED again.

                But when a field object is deleted, the FieldLayer has his flags set to SKIP | HIDE | TEMPORARY. You can retrieve them via FieldLayer::GetLayerFlags.

                After deleting a linked FieldObject, the respective FieldLayer in the FieldList is not set to SKIP or HIDE or TEMPORARY. It is still NONE.

                EDIT: Interesting. I wrote a little Python tag to print out the flags of all FieldLayers in the FieldList of a MoGraph Effector object, and in deed, the flags are set in the Effector's FieldList's FieldLayer. They are not set in my object.

                I guess the EffectorData usually takes care for flagging the dead FieldLayers. My object is not an effector. Is there any message I need to catch and handle?

                For now, I'll try and recognise dead and otherwise unusable FieldLayers like this. but it doesn't feel safe:

                inline Bool IsActiveFieldLayer(FieldLayer *fieldLayer, BaseDocument *doc)
                {
                	// Inactive or not sampling the value (we only need the ones that sample value)
                	const FIELDLAYER_CHANNELFLAG channelFlags = fieldLayer->GetChannelFlags();
                	if (!(channelFlags&FIELDLAYER_CHANNELFLAG::ENABLE) || !(channelFlags&FIELDLAYER_CHANNELFLAG::VALUE))
                		return false;
                
                	// Flagged as no-to-be-used (doesn't seem to work, flags are never set)
                	const FIELDLAYER_FLAG layerFlags = fieldLayer->GetLayerFlags();
                	if (layerFlags&FIELDLAYER_FLAG::SKIP ||
                	    layerFlags&FIELDLAYER_FLAG::ERRORSKIP ||
                	    layerFlags&FIELDLAYER_FLAG::HIDE ||
                	    layerFlags&FIELDLAYER_FLAG::TEMPORARY)
                		return false;
                
                	// Dead link but not a folder (once the flags actually work, the first line can be removed)
                	if (!fieldLayer->GetLinkedObject(doc)._object &&
                	     fieldLayer->GetType() != FLfolder)
                		return false;
                
                	// We can consider the FieldLayer active and usable
                	return true;
                }
                

                Cheers,
                Frank

                www.frankwilleke.de
                Only asking personal code questions here.

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

                  Hi @fwilleke80 looking at the code this is something done in the CheckDirty and FieldData::SetLinkedObjectin all our FieldData.
                  The logic is similar by checking if the link is still valid.

                  So there is no message for that, and this should be handled automatically if in your ObjectData::CheckDirty your properly forward the call to the falloff. Find an example in Deformer update and Fields.

                  Note that this method is not magic and rely on the CheckDirty to be called so I would say even our solution is not the most robust.

                  Cheers,
                  Maxime.

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

                  1 Reply Last reply Reply Quote 0
                  • fwilleke80F
                    fwilleke80
                    last edited by

                    Thank you! ๐Ÿ™‚

                    www.frankwilleke.de
                    Only asking personal code questions here.

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