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

    Regarding DescIDs and Set/GetParameter

    SDK Help
    0
    10
    756
    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
      Helper
      last edited by

      On 01/06/2015 at 05:44, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:    
      Platform:      
      Language(s) :

      ---------
      Hey! Is there a way to work around the DescID simply ignoring a DescLevel with ID zero and
      everything that follows? Why is that? 😢 I have to add 1 to everything to make sure that no
      zero is passed in to a DescLevel...

      DescID descid(MY_PLUGIN_DATA_GET, index + 1, subindex + 1);
      

      And in my GetDParameter(), i have to subtract the 1 again.

      Int32 index = descid[1].id - 1;
      Int32 subindex = descid[2].id - 1;
      
      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 01/06/2015 at 05:54, xxxxxxxx wrote:

        Oh and is there a way to make GetParameter() not do any checks with the Description and stuff? It seems
        like when I use something like DescID(4005, 1, 1)  it will first call GetDParameter() with DescID(4005)  and
        then DescID(4005, 1).

        Thanks!

        Update : Maybe this is a Python thing? I set the parameter from Python to test the handling of the
        value in GetDParameter(). Here's some debug output maybe so you can see what happens:

        [OK] Points[c4d.PROCEDURAL_CHANNEL_PARAM_DATA, 2]
        PROCEDURAL_CHANNEL_PARAM_DATA: DescID({4005, 0, 0})
        PROCEDURAL_CHANNEL_PARAM_DATA: DescID({4005, 23, 0})
        PROCEDURAL_CHANNEL_PARAM_DATA: DescID({4005, 23, 0},{2, 23, 0})
        Vector(-5.207, 145.789, 20)
        

        Currently, in GetDParameter(), I accept DescIDs that have not the required depth and simply
        use a default value for the missing levels instead. Also , it would be nice if GetDParameter() was
        really only called once due to performance reasons.

        	Int32 GetIndexFromDescID(const DescID& id) const
        	{
        		CriticalAssert(mItemLength >= 1 && mItemLength <= PROCEDURAL_CHANNEL_MAXITEMLENGTH);
        		const Int32 depth = id.GetDepth();
        		if (depth > 3) return -1;
        		Int32 elementIndex = (depth >= 2 ? id[1].id - 1 : 0);  // There can not be a DescLevel with id zero,
        		Int32 itemIndex = (depth >= 3 ? id[2].id - 1 : 0);     // we expect the index to be offset by 1.
        		if (elementIndex < 0 || elementIndex >= mCount) return -1;
        		if (itemIndex < 0 || itemIndex >= mItemLength) return -1;
        		return elementIndex * mItemLength + itemIndex;
        	}
        
        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 01/06/2015 at 06:43, xxxxxxxx wrote:

          [SOLVED]

          Another question: Is there a way to prevent SetDParameter() to call C4DAtom::SetDirty(DIRTYFLAGS_DATA)? 😠

          Update : I've worked around this by hooking the Cinema 4D API.

          /// **************************************************************************
          /// **************************************************************************
          class ChannelApiHook
          {
          public:
            /// This hooked API call ensures that, for a Channel Node, a DescID with \var
            /// PROCEDURAL_CHANNEL_PARAM_DATA as the first ID will be routed \em directly
            /// to the underlying \ref NodeData and that \c SetDirty(DIRTYFLAGS_DATA)
            /// will \em not be called on the node for this parameter!
            Bool SetParameter(const DescID& id, const GeData& data, DESCFLAGS_SET flags);
            
            static Bool Register();
          private:
            static decltype(C4DOS.At->SetParameter) s_SetParameter;
          };
            
          DECLMEMBER(ChannelApiHook, s_SetParameter);
            
          /// **************************************************************************
          /// **************************************************************************
          Bool ChannelApiHook::SetParameter(const DescID& id, const GeData& data, DESCFLAGS_SET flags)
          {
            C4DAtom* atom = reinterpret_cast<C4DAtom*>(this);
            if (atom->GetType() == PROCEDURAL_CHANNEL_ID && id[0].id == PROCEDURAL_CHANNEL_PARAM_DATA) {
              GeListNode* node = static_cast<GeListNode*>(atom);
              NodeData* nodeData = node->GetNodeData();
              NODEPLUGIN* table = C4DOS.Bl->RetrieveTableX(nodeData, 0);
              if (nodeData && table) {
                return (nodeData->*table->SetDParameter)(node, id, data, flags);
              }
            }
            return (atom->*s_SetParameter)(id, data, flags);
          }
            
          /// **************************************************************************
          /// **************************************************************************
          Bool ChannelApiHook::Register()
          {
            s_SetParameter = C4DOS.At->SetParameter;
            C4DOS.At->SetParameter = (decltype(s_SetParameter)) &ChannelApiHook::SetParameter;
            return true;
          }
          
          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 01/06/2015 at 07:49, xxxxxxxx wrote:

            Hello,

            could you please provide us with some code or some context that shows what are you doing and what exactly fails?

            Best wishes,
            Sebastian

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

              On 01/06/2015 at 08:00, xxxxxxxx wrote:

              I'll try to explain it, first, and if it will still be helpful, I can create a stripped down version of the
              code tomorrow 🙂

              The Dirty-Count problem is sort of solved. Remaining:

              • DescID() does not accept zero-ID DescLevels
              • GetDParameter() is called multiple times for a single parameter

              I guess the first is a limitation of the current implementation of the DescID class. There might have been
              some reason to it, but if it won't break anything, I would greatly appreciate if this would be changed some day. To give just a little more detail, the following is the problem:

              DescID(3, 0) == DescID(3) && DescID(3, 0 9) == DescID(3);
              DescID(3, 0).GetDepth() == 1;
              

              The latter appears when using the op[id] = value  Syntax in Python, but I have yet to test it using
              C4DAtom::GetParameter()  from C++. If I use an ID that has multiple levels, like DescID(4005, 9, 2) ,
              NodeData::GetDParameter()  is not called once  for the ID, but three times. And if during any of those
              three calls, one does fail to retrieve a value, the others will never be invoked. What you in
              GetDParameter() is

              • DescID(4005)
              • DescID(4005, 9)
              • DescID(4005, 9, 2)

              I want to use a DescID as an array subscript for the data in my plugin and return the data at that index.
              The format I use currently is DescID(4005, row + 1, column + 1). The thing here is that before I get
              passed the DescID that I expect, I get two "invalid" DescIDs. And I have to return a valid value for them,
              otherwise I will never get the third, correct  DescID in NodeData::GetDParameter().

              Mainly this is a performance bottleneck and secondly, I want it to be an error to use a DescID that is
              ill formed (so only DescID(4005)  or DescID(4005, 9)  should not succeed in retrieving a value, but
              DescID(4005, 9, 2)  should).

              Thanks for your help,
              -Niklas

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

                On 01/06/2015 at 11:25, xxxxxxxx wrote:

                Hello,

                abusing the DescID in such ways seems to be a strange hack; what are you really trying to do? It is a feature of Python that GetParameter() is called multiple times when the DTYPE is not defined. I cannot confirm that a DescID with zeros is ignored. Without code it's hard so say anything.

                Best wishes,
                Sebastian

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

                  On 01/06/2015 at 12:26, xxxxxxxx wrote:

                  Hi Sebastian,

                  it doesn't feel too abusing to me. Basically, my plugin is a data container. I want the data to be read
                  and writable via GetParameter() because I will not need to write additional code for Python in order
                  to read and write data to the plugin.

                  It is a feature of Python that GetParameter() is called multiple times when the DTYPE is not defined.

                  Thanks, I'll try it with specifying the datatype!

                  I cannot confirm that a DescID with zeros is ignored. Without code it's hard so say anything.

                  It might as well only be Python behaviour, maybe?

                  c4d.DescID(c4d.DescLevel(3), c4d.DescLevel(0), c4d.DescLevel(9)).GetDepth()
                  

                  Thanks,
                  Niklas

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

                    On 02/06/2015 at 01:27, xxxxxxxx wrote:

                    Hello,

                    I still cannot confirm the described behavior. Please provide more context or code.

                    From what you tell I'm wondering why you are not simply using the parameter ID itself as your array subscript. This parameter ID could be calculated on the "coordinates" you use like the index of a pixel in an image with a given width and height.

                    Best wishes,
                    Sebastian

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

                      On 02/06/2015 at 01:49, xxxxxxxx wrote:

                      Hi Sebastian,

                      what does it print out for your if you run this line in the console? Does it print 1? That is what it
                      prints for me. And that means the second and third level of the DescID are simply ignored. Or am I
                      missing something?

                      c4d.DescID(c4d.DescLevel(3), c4d.DescLevel(0), c4d.DescLevel(9)).GetDepth()
                      

                      From what you tell I'm wondering why you are not simply using the parameter ID itself as your array subscript. This parameter ID could be calculated on the "coordinates" you use like the index of a pixel in an image with a given width and height.

                      I'm not sure if I understand you correctly. You mean a single ID that is decomposed in two numbers
                      that is then used as array subscript?

                      Please provide more context or code.

                      This is my GetDParameter() and SetDParameter() implementation:

                      /// **************************************************************************
                      /// **************************************************************************
                      Bool ChannelPlugin::GetDParameter(
                        GeListNode* node, const DescID& id, GeData& data, DESCFLAGS_GET& flags)
                      {
                        switch (id[0].id) {
                          // ...
                          // ...
                          case PROCEDURAL_CHANNEL_PARAM_DATA:
                            // If this parmaeter is accessed, a second description ID level
                            // must be present representing the index of the element to be
                            // accessed. Subtract by one because a zero DescLevel does not exist.
                            if (GetElement(id, data)) {
                              flags |= DESCFLAGS_GET_PARAM_GET;
                              return true;
                            }
                            else {
                              const String name = static_cast<BaseObject*>(node)->GetName();
                              GePrint("[" + name + "]: Get PARAM_DATA: invalid DescID " + nr::ToString(id));
                            }
                            return false;
                          case PROCEDURAL_CHANNEL_UI_MEMINFO:
                            data.SetString(mData.ToString());
                            flags |= DESCFLAGS_GET_PARAM_GET;
                            return true;
                        }
                        return SUPER::GetDParameter(node, id, data, flags);
                      }
                        
                      /// **************************************************************************
                      /// **************************************************************************
                      Bool ChannelPlugin::SetDParameter(
                        GeListNode* node, const DescID& id,
                        const GeData& data, DESCFLAGS_SET& flags)
                      {
                        switch (id[0].id) {
                          // ...
                          // ...
                          case PROCEDURAL_CHANNEL_PARAM_DATA:
                            if (mLocked) return false;
                            if (SetElement(id, data)) {
                              flags |= DESCFLAGS_SET_PARAM_SET;
                              return true;
                            }
                            else {
                              const String name = static_cast<BaseObject*>(node)->GetName();
                              GePrint("[" + name + "]: Set PARAM_DATA: invalid DescID " + nr::ToString(id));
                            }
                            return false;
                        }
                        return SUPER::SetDParameter(node, id, data, flags);
                      }
                      

                      Here's what GetElement() and SetElement() looks like (also class members).
                      The overloads of these methods that are called are not of interest.

                       inline Bool GetElement(const DescID& id, GeData& data) const
                        {
                          Int32 index = GetIndexFromDescID(id);
                          if (index >= 0) return GetElement(index, data);
                          return false;
                        }
                        
                        inline Bool SetElement(const DescID& id, const GeData& data) const
                        {
                          Int32 index = GetIndexFromDescID(id);
                          if (index >= 0) return SetElement(index, data);
                          return false;
                        }
                        
                        /// Computes a linear index from a \class DescID that attempts to access
                        /// a sub-element of the channel. Returns a negative value if the DescID
                        /// is invalid or out of range.
                        Int32 GetIndexFromDescID(const DescID& id) const
                        {
                          CriticalAssert(mItemLength >= 1 && mItemLength <= PROCEDURAL_CHANNEL_MAXITEMLENGTH);
                          const Int32 depth = id.GetDepth();
                          if (depth > 3) return -1;
                          Int32 elementIndex = (depth >= 2 ? id[1].id - 1 : 0);  // There can not be a DescLevel with id zero,
                          Int32 itemIndex = (depth >= 3 ? id[2].id - 1 : 0);     // we expect the index to be offset by 1.
                          if (elementIndex < 0 || elementIndex >= mCount) return -1;
                          if (itemIndex < 0 || itemIndex >= mItemLength) return -1;
                          return elementIndex * mItemLength + itemIndex;
                        }
                      

                      Best regards,
                      Niklas

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

                        On 02/06/2015 at 10:37, xxxxxxxx wrote:

                        Hello,

                        that GetDepth() returns 1 seems to be no malfunction but intended behavior.

                        As said, I guess the best approach would be to use the actual parameter ID as the index. Based on that index you could create you coordinates, like here.

                        Best wishes,
                        Sebastian

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