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

    Get Group description children

    SDK Help
    0
    22
    11.8k
    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 28/05/2016 at 12:48, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:   17 
      Platform:   Windows  ;   
      Language(s) :     C++  ;

      ---------
      May sound trivial, but how do I get all Description elements that reside within a group?

      For example:

        
      ...   
        
      GROUP MY_GROUP   
          {   
              ELEMENT1 {}   
              ELEMENT2 {}   
              ELEMENT3 {}   
        
              ...   
          }   
      

      I just want to get their names an their data state (e.g. Bool - checked/unchecked)

      I just can't wrap my head around that.

      edit: Found this thread: https://plugincafe.maxon.net/topic/9198/12218_browse-through-description-levels-solved&PID;=48478#48478

      Seems like that's the solution. Is it okay, to use LoadDescription inside GetDDescription of a node?

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

        On 28/05/2016 at 15:22, xxxxxxxx wrote:

        If you implement GetDDescription() you must call LoadDescription() to load static descriptions of the node:

        if (!description->LoadDescription(node->GetType()))
        		return FALSE;
        

        The Description browsing methods are probably the best way to get at descriptions by group.

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

          On 28/05/2016 at 15:33, xxxxxxxx wrote:

          Yes, my descriptions are already loaded.

          I should have been a bit more precise, I guess.
          Now I just need to get these elements as shown in the first post. Using "LoadDescription()" twice seems to be wrong.
          The description already exists, so the only thing needed here is to access these DescLevel elements.

          Let me clearify what I want to do: I have a BaseList2D object with a description. A couple of checkboxes insinde a simple group. Now there's a LONG cycle that should yield the currently checked items and display their Description names. (DESC_NAME)

          Checking against the group whilst browsing through the description led to weird results. The string entries were visible, but not selectable.

          I will post some code if this helps to help me. 🙂

          edit:
          Here's some code from my actual GetDDescription method:

            
          ...   
                  // Update available mat slot assignments   
               BaseContainer* slotBc = description->GetParameterI(DescLevel(XNB_PASS_BASE_MATERIAL_SLOT_DIFFUSE), nullptr);   
            
               if (slotBc)   
               {   
                    BaseContainer slotMode;   
                    void* handle = description->BrowseInit();   
                    const BaseContainer* bc = nullptr;   
                    DescID id, groupid;   
            
                    while (description->GetNext(handle, &bc;, id, groupid))   
                    {   
                         if (bc)   
                         {   
                              if (groupid == XNB_PASS_BASE_ENABLE_GROUP)   
                              {   
                                   pass->GetParameter(id, data, DESCFLAGS_GET_0);   
                                   if (data.GetBool())   
                                        slotMode.SetString(id[0].id, bc->GetString(DESC_NAME));   
                              }   
                         }   
                    }   
                    description->BrowseFree(handle);   
                    slotBc->SetContainer(DESC_CYCLE, slotMode);   
               }   
          ...   
          
          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 29/05/2016 at 12:15, xxxxxxxx wrote:

            This code is a bit old-school (from an R13 project), but it shows 'dynamic' creation of cycle elements in a description LONG CYCLE (drop-down).  Part of your problem might be the use of 'id[0].id' as the index into the drop-down.  Typically I start container indices at 2000.

            GetDDescription()
            {
            	BaseContainer*	opBC =		((BaseTag* )node)->GetDataInstance();
            	if (!opBC)		return FALSE;
            	BaseDocument*	doc =		node->GetDocument();
            	if (!doc)		return FALSE;
            	...
            	// Collect BodyParts into Drop-Down list and add dials for selected BodyPart
            	// - Retrieve or Create SubContainer for Links to Figure's Body Parts (iPP Bases)
            	subBC =						opBC->GetContainerInstance(ID_BODYPARTS);
            	if (subBC == nullptr)
            		return FALSE;
            	if (!CollectBodyParts(doc, orig, opBC))
            		return FALSE;
            	...
            }
              
            // IPPFigure.CollectBodyParts
            //*---------------------------------------------------------------------------*
            Bool IPPFigure::CollectBodyParts(BaseDocument* doc, BaseObject* root, BaseContainer* tagBC)
            //*---------------------------------------------------------------------------*
            {
            	// - Retrieve or Create SubContainer for Links to Figure's Body Parts (iPP Bases)
            	subBC =			tagBC->GetContainerInstance(ID_BODYPARTS);
            	if (!subBC)
            	{
            		BaseContainer	mbc(ID_BODYPARTS);
            		tagBC->SetContainer(ID_BODYPARTS, mbc);
            		subBC =			tagBC->GetContainerInstance(ID_BODYPARTS);
            		if (subBC == nullptr)
            				return ErrorException::Throw(EE_DIALOG, GeLoadString(IPPERR_MEMORY_TEXT), "IPPFigure.CollectBodyParts.subBC");
            	}
            	// Collect iPP Bases into SubContainer as BaseLinks
            	bpIndex =		FIRST_BPINDEX;
            	CollectRecurse(doc, root, root->GetDown());
            	tagBC->SetInt32(IPPFIGURE_BPINDEX, bpIndex);
            	return TRUE;
            }
            // IPPFigure.CollectRecurse - forward recursive, depth-first
            //*---------------------------------------------------------------------------*
            void IPPFigure::CollectRecurse(BaseDocument* doc, BaseObject* root, BaseObject* obj)
            //*---------------------------------------------------------------------------*
            {
            	BaseContainer*	bc =		nullptr;
            	for (; obj; obj = obj->GetNext())
            	{
            		if (obj->IsInstanceOf(ID_IPPBASE))
            		{
            			bc = obj->GetDataInstance();
            			if (bc && (bc->GetLink(IPP_ROOT, doc) == root))
            			{
            				// Add a BaseLink to ID_BODYPARTS sub-BaseContainer
            				subBC->SetLink(bpIndex,	obj);
            				++bpIndex;
            			}
            		}
            		if (obj->GetDown())	CollectRecurse(doc, root, obj->GetDown());
            	}
            }
            
            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 29/05/2016 at 22:59, xxxxxxxx wrote:

              Hi Robert,

              thanks a lot for your example.
              It shows me another approach without having to browse the whole description.

              Part of your problem might be the use of 'id[0].id' as the index into the drop-down. Typically I start container indices at 2000.

              I thought the same but it turned out that manually incrementing the index started at 0 has the same issue.

              However, I'll have a deeper look into it tonight.

              Thanks for now. 😉

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

                On 30/05/2016 at 02:03, xxxxxxxx wrote:

                Just want to add, that' there's quite an elaborate example on dynamic descriptions: objectdata_descriptions.cpp

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

                  On 30/05/2016 at 14:53, xxxxxxxx wrote:

                  Thank you all very much for your input. 
                  I really wasn't able to figure out what causes the issue.

                  However, I'm now heading for another approach.

                  If anybody is willing to solve the riddle or is willing to explain why it doesn't work, feel free to test this code:
                  (Maybe it has something to do with the Browsing itself, because it works outside the browse loop)

                  	BaseContainer* slotBc = description->GetParameterI(DescLevel(XNB_PASS_BASE_MATERIAL_SLOT_DIFFUSE, DTYPE_LONG, 0), nullptr); // The target element
                  	if (slotBc != nullptr)
                  	{
                  		BaseContainer* items = slotBc->GetContainerInstance(DESC_CYCLE); // Directly access the already available items (none)
                    
                  		void* handle = description->BrowseInit(); // Prepare browsing
                  		const BaseContainer* bc = nullptr; // temp container
                  		DescID id, groupid; // accessors
                  		Int32 index = 0; // counter for enumeration
                    
                  		while (description->GetNext(handle, &bc, id, groupid))
                    
                  		{
                  			items->SetString(++index, bc->GetString(DESC_NAME)); // Set index to counter increment, display name to description name
                  		}
                  		description->BrowseFree(handle);	// Dealloc
                  	}
                    
                  	flags |= DESCFLAGS_DESC_LOADED;
                    
                  
                  

                  Cheers!

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

                    On 01/06/2016 at 11:11, xxxxxxxx wrote:

                    Just wanted to let you know, that I can reproduce your issue, but haven't found the culprit, yet.

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

                      On 01/06/2016 at 14:25, xxxxxxxx wrote:

                      Hmm, the docs say the caller owns the pointed container, so I guess it must be instanced first? But that would mean the pointed container is only "filled" (though it could also mean otherwise, as I remember I used description->GetDescEntry() before with a nullptr initialisation of the temp basecontainer and it worked fine..hmmm).
                      The function signature of GetNext() is kinda ugly.

                        
                            BaseContainer bc;  
                            const BaseContainer* bcptr = &bc;  
                        
                            while (desc->GetNext(handle, &bcptr, id, groupid))
                      

                      Note: I haven't tried this code in practice. And now after writing it kind of doesn't seem right...just grabbing thoughts.

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

                        On 01/06/2016 at 15:10, xxxxxxxx wrote:

                        For Description::GetDescEntry() and GetNext(), the argument is const BaseContainer**.  So, &bcptr; is correct (a pointer to a pointer to a BaseContainer).

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

                          On 02/06/2016 at 00:26, xxxxxxxx wrote:

                          @Samir: Unfortunately the docs are wrong, C4D owns the pointed BaseContainer. I discovered the error yesterday afternoon. Will be corrected in the next release of the SDK Docs. But this is not the reason for the actual issue of this thread. Still looking into it.

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

                            On 02/06/2016 at 03:32, xxxxxxxx wrote:

                            @Kuro: Cheers. What I actually meant was if C4D is actually filling the pointer pointed to with an own instance pointer (allocated internally in the GetNext() function) and the user must free this one or if the pointer comes from the caller (my version). But as the docs are apparently wrong (thanks Andreas for the info), this resolves that it should be nullptr initialised. : )

                            @Andreas: thx. Hmm, what is the actual issue of the code then? What is not working with the provided example?

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

                              On 03/06/2016 at 02:52, xxxxxxxx wrote:

                              Originally posted by xxxxxxxx

                              Just wanted to let you know, that I can reproduce your issue, but haven't found the culprit, yet.

                              Glad to hear. 
                              Thanks for your efforts. 🙂

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

                                On 03/06/2016 at 10:19, xxxxxxxx wrote:

                                @mp5gosu: I'm sorry, I wasn't able to find the issue until now. Unfortunately I will be at our developer meeting, so surely won't make any progress during the next week. I'm planning to continue to work on this after my return. By experience I already know, that it will get a bit turbulent, when the team has been off for an entire week, so please ping me again, if I don't return to you within a reasonable amount of time (lets say Wednesday?). Terribly sorry!

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

                                  On 03/06/2016 at 10:27, xxxxxxxx wrote:

                                  Hey Andreas, no worries! I know you Maxonians are busy but dedicated people. 😉
                                  Sure, I'll note you.

                                  However, thanks again for having a look into this issue. 
                                  It's not crucial for me right now but would make me write way less code. Also for others this would help solving some problems regarding this issue.

                                  Cheers, have a nice weekend and have some fun on the DevMeet!

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

                                    On 13/06/2016 at 22:50, xxxxxxxx wrote:

                                    Here's your personal reminder. 😉
                                    Did you find some time to sort this out yet?

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

                                      On 14/06/2016 at 00:38, xxxxxxxx wrote:

                                      No, I haven't yet. But I haven't forgotten either. Nevertheless thanks for the reminder. I'll continue to investigate today.

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

                                        On 14/06/2016 at 05:33, xxxxxxxx wrote:

                                        TL;DR but here's a class that I built for the Vray project that allows to browse all parameters in the
                                        description and check the parent group. For the way I use it, it's working very good. You may use it
                                        as you like. Usage is pretty straight forward:

                                        vb::desc::StructureHelper structure(description);
                                        for (auto const* info : structure.iter()) {
                                        	if (structure.CheckGroup(info->did, THE_GROUP_ID)) {
                                        		// This parameter is inside the group THE_GROUP_ID
                                        		// ...
                                        	}
                                        }
                                        

                                        In case you don't want to skip the iterator part, nr_iterator is here.

                                        /// Copyright(C) 2015  Niklas Rosenstein
                                        /// Developed for laubLAB KG.
                                        ///
                                        /// Source/Library/lib_desc.h
                                          
                                        #pragma once
                                        #include "c4d.h"
                                        #include "nr/iterator.h"
                                          
                                        namespace vb {
                                        namespace desc {
                                          
                                        	/**
                                        	 * Structure that contains the parameter information as it is yielded by
                                        	 * #Description::GetNext().
                                        	 */
                                        	struct ParamInfo {
                                        		BaseContainer const* bc = nullptr;
                                        		DescID did;
                                        		DescID gid;
                                        	};
                                          
                                        	/**
                                        	 * This is a helper class for reading structural information from a node's
                                        	 * #Description. For instance, it allows you to check if a parameter is inside
                                        	 * a specific group.
                                        	 */
                                        	class StructureHelper {
                                        	private:
                                        		friend class Iterator;
                                        		maxon::HashMap<Int32, ParamInfo> map;
                                        		maxon::BaseArray<Int32> order;  // required for correct order iteration
                                        	public:
                                        		StructureHelper() { }
                                        		StructureHelper(Description* desc) { Init(desc); }
                                        		~StructureHelper() { }
                                          
                                        		inline Bool Init(Description* desc) {
                                        			this->map.Flush();
                                        			this->order.Flush();
                                        			if (!desc) return false;
                                        			ParamInfo param;
                                        			void* handle = desc->BrowseInit();
                                        			while (desc->GetNext(handle, &param.bc, param.did, param.gid)) {
                                        				this->map.Put(param.did[0].id, param);
                                        				this->order.Append(param.did[0].id);
                                        			}
                                        			desc->BrowseFree(handle);
                                        			return true;
                                        		}
                                          
                                        		/**
                                        		 * Returns the #ParamInfo info structure for a specific description ID
                                        		 * #did or nullptr if there is no parameter with the specified ID.
                                        		 */
                                        		// @{
                                        		inline ParamInfo const* Get(Int32 did) const {
                                        			auto* entry = this->map.FindEntry(did);
                                        			if (entry) return &entry->GetValue();
                                        			return nullptr;
                                        		}
                                          
                                        		inline ParamInfo const* Get(DescID const& did) const {
                                        			return this->Get(did[0].id);
                                        		}
                                        		// @}
                                          
                                        		/**
                                        		 * Returns the #ParamInfo for the parent group of the specified parameter.
                                        		 */
                                        		inline ParamInfo const* GetParentGroup(ParamInfo const* info) const {
                                        			if (!info || info->did[0].id == info->gid[0].id) return nullptr;
                                        			return this->Get(info->gid);
                                        		}
                                          
                                        		/**
                                        		 * Checks if the parameter with the description ID #did is anywhere
                                        		 * in the group specified by #gid and returns true in that case, false
                                        		 * otherwise.
                                        		 */
                                        		inline Bool CheckGroup(DescID did, DescID const& gid) const {
                                        			while (did != gid) {
                                        				ParamInfo const* group = this->Get(did);
                                        				if (!group) return false;
                                        				did = group->gid;
                                        			}
                                        			return true;
                                        		}
                                          
                                        		/**
                                        		 * Helper to iterate over the entries in the description. This will
                                        		 * happen in the same order as the iteration over the source #Description.
                                        		 */
                                        		class Iterator : public nr::iterator<Iterator> {
                                        		private:
                                        			StructureHelper const& helper;
                                        			decltype(StructureHelper::order) ::ConstIterator it, itend;
                                        		public:
                                        			Iterator(StructureHelper const& helper) : helper(helper), it(helper.order.begin()), itend(helper.order.end()) { }
                                        			Bool at_end() const { return it == itend; }
                                        			ParamInfo const* operator * () const { return helper.Get(*it); }
                                        			void next() { ++it; }
                                        		};
                                          
                                        		inline Iterator iter() const { return Iterator(*this); }
                                          
                                        	};
                                          
                                        }} // namespace vb::desc
                                        
                                        1 Reply Last reply Reply Quote 0
                                        • H
                                          Helper
                                          last edited by

                                          On 14/06/2016 at 09:55, xxxxxxxx wrote:

                                          Thanks Niklas. Storing the data into a struct seems like a reasonable approach. I'll give it a try if I find some time. 😉

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

                                            On 30/06/2016 at 03:42, xxxxxxxx wrote:

                                            Hey Andreas, any progress here yet? 😉

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