Group Details Private

Global Moderators

Forum wide moderators

  • RE: Default startup settings

    hello,

    if you change a parameter in your dialog box, it will trigger the Command function see this manual for dialog box interaction

    You can do the same there, get the WorldContainer that is a BaseContainer (see manual) and change the data inside it.

    You could have a button on your dialog (or a menu) that say "set default". Or when you change a DropBox.
    That's up to you. But you should keep the preference part just to let people know it's there or if they want to create script that will change them.

    Cheers
    Manuel

    posted in Cinema 4D Development
  • RE: Default startup settings

    Hello,

    well, if you want to store information that last between documents and once you switch off cinema4D and relaunch it :
    i see two solutions :

    • The first one is to use the WorldContainer() (The preferences dialog box use that to store its data)
      You have to use PrefsDialogObject::Register to register your preferences, and it's pretty simple. Use GetDDescription to react to the "ask default" interaction, GetDParameter and SetDParameter to store the data. Using also Init and InitValues.
      You need to store your data in a BaseContainer inside the WorldContainer. You have to store it in your own ID (it has to be unique so grab it on plugincafe's page . You can use the same as your plugins's ID but's you can use a different one. It just have to be unique
      The problem is that you can only store Maxon data type.
      You can change those values anywhere from cinema4D.

    • Another possibility, as zipit said, is to create your own file structure. The advantage is that you can store what ever data you need. It can be binary, text...
      Another advantage is that you can easily "copy paste" and share them from computer to computer. (you don't have to create "export/import" command like with the world Container)
      You could load that file on PluginStart and store that in memory or read it when ever you need it (to be determine) and write the file on PluginEnd

    If something is not clear, just ask, I may have forgotten something.

    I've created a simple example for the preferences :

    #include "c4d_basedocument.h"
    #include "lib_prefs.h"
    #include "c4d_customdatatypeplugin.h"
    #include "c4d_general.h"
    #include "Ppc11737_Pref.h"
    
    // Unique plugin ID for world preference container obtained from www.plugincafe.com
    #define WPREF_ID 1053240
    
    
    class PC11737_Pref : public PrefsDialogObject
    {
    	INSTANCEOF(PC11737_Pref, PrefsDialogObject)
    
    public:
    	static NodeData* Alloc() { return NewObjClear(PC11737_Pref); }
    
    
    	virtual Bool Init(GeListNode* node) 
    	{
    		InitValues(MY_CYCLE);
    		InitValues(ANOTHER_LONG);
    		return true;
    	}
    
    	virtual Bool InitValues(const DescID &id, Description *description = nullptr) 
    	{
    		// Retrieves our container inside the world Container
    		BaseContainer* bc = GetMyPreference();
    		if (bc == nullptr)
    			return false;
    
    		switch (id[0].id)
    		{
    			case MY_CYCLE:
    			{
    				InitPrefsValue(MY_CYCLE, GeData(FIRST_VALUE), description, id, bc);
    				break;
    			}
    			case ANOTHER_LONG:
    			{
    				InitPrefsValue(ANOTHER_LONG, GeData(150), description, id, bc);
    				break;
    			}
    		}
    
    		return true;
    	}
    
    	virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags) 
    	{
    		
    		if (!description->LoadDescription(WPREF_ID))
    			return false;
    
    		// Checks if the default value have been asked by a right click -> "default value"
    		if (flags & DESCFLAGS_DESC::NEEDDEFAULTVALUE)
    		{
    			InitValues(MY_CYCLE, description);
    			InitValues(ANOTHER_LONG, description);
    		}
    
    		flags |= DESCFLAGS_DESC::LOADED;
    
    		return SUPER::GetDDescription(node, description, flags);
    	}
    
    	virtual Bool GetDParameter(GeListNode* node, const DescID& id, GeData& t_data, DESCFLAGS_GET& flags) 
    	{
    		// Retrieves our container inside the world Container
    		BaseContainer* bc = GetMyPreference();
    		if (bc == nullptr)
    			return SUPER::GetDParameter(node, id, t_data, flags);
    
    		switch (id[0].id)
    		{
    			case MY_CYCLE:
    			case ANOTHER_LONG:
    			{
    				t_data = bc->GetInt32(id[0].id);
    				flags |= DESCFLAGS_GET::PARAM_GET;
    				return true;
    				break;
    			}
    		}
    
    
    		return SUPER::GetDParameter(node, id, t_data, flags);
    	}
    
    
    	virtual Bool SetDParameter(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_SET& flags) 
    	{
    		// Retrieves our container inside the world Container
    		BaseContainer* bc = GetMyPreference();
    		if (bc == nullptr)
    			return SUPER::SetDParameter(node, id, t_data, flags);
    
    		switch (id[0].id)
    		{
    			case MY_CYCLE:
    			case ANOTHER_LONG:
    			{
    				bc->SetInt32(id[0].id, t_data.GetInt32());
    				flags |= DESCFLAGS_SET::PARAM_SET;
    				GeUpdateUI();
    				return true;
    				break;
    			}
    		}
    
    		return SUPER::SetDParameter(node, id, t_data, flags);;
    	}
    
    
    	virtual Bool GetDEnabling(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_ENABLE flags, const BaseContainer* itemdesc) 
    	{
    		return SUPER::GetDEnabling(node, id, t_data, flags, itemdesc);
    	}
    
    private: 
    	BaseContainer* GetMyPreference()
    	{
    		// Retrieves our container inside the world Container
    		BaseContainer* bc = GetWorldContainerInstance()->GetContainerInstance(WPREF_ID);
    		// Doesn't exist ? Create it
    		if (!bc)
    		{
    				GetWorldContainerInstance()->SetContainer(WPREF_ID, BaseContainer());
    				bc = GetWorldContainerInstance()->GetContainerInstance(WPREF_ID);
    				if (!bc) return nullptr;
    		}
    
    		return bc;
    	}
    
    };
    
    static maxon::Result<void> RegisterPC11737()
    {
    	iferr_scope;
    
    
    	if (!PrefsDialogObject::Register(WPREF_ID, PC11737_Pref::Alloc, "My pref"_s, "Ppc11737_Pref", 0, 0))
    		return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
    
    	return maxon::OK;
    }
    
    

    .res

    CONTAINER Ppc11737_Pref
    {
    	NAME Ppc11737_Pref;
    
    
    	GROUP
    	{
            LONG MY_CYCLE { 
    
            	CYCLE
            	{
            		FIRST_VALUE;
            		SECOND_VALUE;
            	}
              }
            LONG ANOTHER_LONG {}
    	}
    }
    
    

    .h

    #ifndef _PPC11737_PREF_
    #define _PPC11737_PREF_
    
    enum
    {
    	FIRST_VALUE 	= 1,
       	SECOND_VALUE 	= 2,
     	MY_CYCLE 		= 1000,
     	ANOTHER_LONG
    
    };
    
    #endif // _PPC11737_PREF_
    

    .str

    STRINGTABLE Ppc11737_Pref
    {
    	Ppc11737_Pref                      "My preferences";
    	MY_CYCLE				"This is my cycle";
     	FIRST_VALUE 					 	"My First Value";
       	SECOND_VALUE 					 	"My Second Value";
       	ANOTHER_LONG						"Another Value";
    }
    
    

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: Moving a group of spline points

    hello,

    happy you found your solution, and thanks for the feedback :)

    Cheers
    Manuel

    posted in Cinema 4D Development
  • RE: Change an attribute from a dialogue plug-in

    Hello,

    as I said above: you rebuild your UI after each EVMSG_CHANGE. Thus, you loose the focus of your UI elements (since they are destroyed and rebuilt).

    You have to find a way to avoid that unnecessary rebuild of your layout after user interaction in your dialog.

    best wishes,
    Sebastian

    posted in Cinema 4D Development
  • RE: Retrieving particle positions for Multi-Instance

    hello,

    so you want to retrieve the positions of particles (from particles object or TP) ?

    About Thinking Particles you have information here, don't forget to use InitThinkingParticles before using it.

    For Particles Object you have this page and this is pretty straight forward.

    For example :

            // Retrieves Particles Object
    
    	BaseObject *selObj = doc->GetActiveObject();
    	
    	
    	if (selObj == nullptr || !selObj->IsInstanceOf(Oparticle))
    	{
    		MessageDialog("Need to select a particle object"_s);
    		return maxon::OK;
    	}
    
    
    	// cast object to ParticleObject
    	ParticleObject* partObj = static_cast<ParticleObject*>(selObj);
    	if (partObj == nullptr)
    		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
    
    	const maxon::Int32 cnt = partObj->GetParticleCount();
    
    	// Retrieves the particle tag that is invisible.
    	ParticleTag* partTag = static_cast<ParticleTag*>(partObj->GetTag(Tparticle));
    
    	for (maxon::Int32 i = 0; i < cnt; i++)
    	{
    		const Particle* myParticle = partObj->GetParticleR(partTag, i );
    		// Check particle's age before output position
    		if (myParticle->t > 0)
    			DiagnosticOutput("particle position @ have age @", myParticle->off, myParticle->t);
    	}
    
    
    	
    	// Creates particles in TP
    
    	// Retrieves the TP Master
    	BaseSceneHook* hook = doc->FindSceneHook(ID_THINKINGPARTICLES);
    	if (hook == nullptr || hook->GetType() != ID_THINKINGPARTICLES)
    		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
    
    	TP_MasterSystem* tpmaster = static_cast<TP_MasterSystem*>(hook);
    
    	if (tpmaster == nullptr)
    		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
    
    	// Alloc 10 particles 
    	maxon::Int32 ids[10];
    
    	if (tpmaster->AllocParticles(10, ids) == NOTOK)
    		return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
    
    	// Changes position of particles and retrieves it to output it.
    	for (auto pid : ids)
    	{
    		tpmaster->SetPosition(pid, Vector(pid * 50));
    		Vector pos = tpmaster->Position(pid);
    		DiagnosticOutput("position of particle @ is @", pid, pos);
    	}
    
    
    	EventAdd();
    

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: Bitmaps in custom shaders don't appear in Texture Manager

    Hello,

    I tested it with the Gradient Shader example from the cinema4dsdk and it works fine as far as I can tell.

    I just removed the check for ASSETDATA_FLAG::TEXTURESONLY:

    Bool SDKGradientClass::Message(GeListNode* node, Int32 type, void* data)
    {
      switch (type)
      {
      case MSG_GETALLASSETS:
      {
        AssetData* const assetData = static_cast<AssetData*>(data);
        // checks if only texture assets should be added
        if (assetData == nullptr)
          return true;
          
        BaseList2D* const baseList2D = static_cast<BaseList2D*>(node);
        if (baseList2D != nullptr)
        {
          // get the file name
          const Filename path = baseList2D->GetData().GetFilename(GRAD_FILE);
          // check if the file name is set
          if (path.IsPopulated())
          {
            // add the file name
            assetData->Add(path, nullptr);
          }
        }
        break;
      }
      }
    
      return SUPER::Message(node, type, data);
    };
    

    Regarding Shader hierarchies: I guess you can send the message to your child-shaders yourself, to make sure they receives the message.

    best wishes,
    Sebastian

    posted in Cinema 4D Development
  • RE: Change an attribute from a dialogue plug-in

    Alternatively, you can check if the ENTER key way pressed and only update when this is the case (the user finished entering the string, see Interaction with Gadgets).

    # get state of the "Enter" key.
    state = c4d.BaseContainer()
    res = c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.KEY_ENTER, state)
    
    if res:
      if state[c4d.BFM_INPUT_VALUE] != 1:
        return False
    
      idx = id - 100000
    
      newString = self.GetString(id)
    
      obj = self.objectsList[idx]
      if obj is not None:
          c4d.StopAllThreads()
          obj.SetName(newString)
          c4d.EventAdd()
    

    This way, the dialog would behave similiar to the Attribute Manager.

    best wishes,
    Sebastian

    posted in Cinema 4D Development
  • RE: Change an attribute from a dialogue plug-in

    Hello,

    you have to call EventAdd() to inform Cinema that you have changed something.

    But in your code, you call your Update() function after a EVMSG_CHANGE message. In your Update() function, you delete and rebuild your UI, thus you loose the focus on the current UI gadget.

    You must find a way to either not call Update() after you edit the name of the object or to restore the focus on the newly created UI.

    best wishes,
    Sebastian

    posted in Cinema 4D Development
  • RE: Default startup settings

    hello,

    Are you asking for a plugin you are creating or a more general part of cinema 4D ?
    What kind of plugin are you creating ? (dialog box, generator, tag, something else)

    Could you also add a tag to know if you are using python or c++ :)

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: [Beginner:Python] Turn object into a point selection bounding box

    hello,

    If you have nothing to add, this thread will be considered as "solved" tomorrow.

    Cheers,
    Manuel

    posted in Cinema 4D Development