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
    • Register
    • Login
    1. Home
    2. fwilleke80
    • Profile
    • Following 0
    • Followers 0
    • Topics 103
    • Posts 450
    • Best 28
    • Controversial 0
    • Groups 0

    Frank Willeke

    @fwilleke80

    Software Engineer

    34
    Reputation
    348
    Profile views
    450
    Posts
    0
    Followers
    0
    Following
    Joined Last Online
    Website www.frankwilleke.de Location Berlin Age 44

    fwilleke80 Unfollow Follow

    Best posts made by fwilleke80

    • RE: Best way to update objects after preference change?

      Oh wait, I think I found a way. In case anybody else wants to know, here it is...

      In the PrefsDialogObject:

      Bool MyPrefsDialog::SetDParameter(GeListNode *node, const DescID &id,const GeData &t_data,DESCFLAGS_SET &flags)
      {
      	BaseContainer* bc = MyPlugin::GetPreferences();
      	if (!bc)
      		SUPER::SetDParameter(node, id, t_data, flags);
      	
      	switch (id[0].id)
      	{
      		// If PREFS_MYPLUGIN_SOMEVALUE was changed, store value and notify plugin objects in all open documents.
      		case PREFS_MYPLUGIN_SOMEVALUE:
      			bc->SetInt32(PREFS_MYPLUGIN_SOMEVALUE, t_data.GetInt32());
      			flags |= DESCFLAGS_SET::PARAM_SET;
      			
      			// Iterate open documents
      			for (BaseDocument *doc = GetFirstDocument(); doc; doc = doc->GetNext())
      			{
      				// Send broadcast message to each document, use unique ID
      				doc->MultiMessage(MULTIMSG_ROUTE::BROADCAST, MyPlugin::UNIQUE_ID_PREFS, nullptr);
      			}
      			
      			GeUpdateUI();
      			return true;
      	}
      	
      	return SUPER::SetDParameter(node, id, t_data, flags);
      }
      

      And then, in the plugin object:

      Bool MyPluginObject::Message(GeListNode *node, Int32 type, void *data)
      {
      	if (type == MyPlugin::UNIQUE_ID_PREFS)
      	{
      		GePrint("Aha! My prefs have changed!"_s);
      		return true;
      	}
      	return SUPER::Message(node, type, data);
      }
      
      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Python Source Protector: Can it be called via CLI?

      And in deed, having the option of executing the source protector using a command line argument for Cinema 4D, or using c4dpy would be great for integration in a plugin build pipeline.

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Custom Tokens with Team Render Server

      Hi,
      I'll just chime in here, as I'm involved in that project, too.

      So the token hook needs to access certain elements of the scene to get their values, and the problem (thankfully) is easily reproducible.

      I wrote a sample plugin that recreates the behaviour by simply returning data from the first object in the scene. Download it from our dropbox: tokenhookbug.zip

      Here is the code:
      tokenhookbug_code.png

      This is the render setting in my example scene (which just contains a Cube):
      tokenhookbug_rendersetting.png

      Here is the render result in the Picture Viewer, notice the correct file name:
      tokenhookbug_result_pv.png

      Uploading and rendering the file on TeamRender produces this:
      tokenhookbug_result_tr.png

      The debugger clearly shows that doc->GetFirstObject() returns nullptr.

      Cheers,
      Frank

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Shader that gets data from an object: Refresh

      It works like a charm!
      Thank you again!

      I was surprised at how little code was required.

      Sharing is caring. In case anyone needs it, here's the code:

      #include "ge_prepass.h"
      #include "c4d_general.h"
      #include "c4d_baselinkarray.h"
      #include "c4d_basedocument.h"
      
      ///
      /// \brief Registers observers and sends messages to them.
      ///
      class Observable
      {
      public:
      	///
      	/// \brief Subscribes a new observer.
      	///
      	/// \param[in] observer Pointer to an AtomGoal
      	/// \param[in] doc The document that owns the AtomGoal
      	///
      	/// \return A maxon error object if anything went wrong, otherwise maxon::OK
      	///
      	maxon::Result<void> Subscribe(C4DAtomGoal *observer, BaseDocument *doc);
      		
      	///
      	/// \brief Unsubscribes an observer
      	///
      	/// \param[in] observer Pointer to an AtomGoal that has previously been subscribed
      	/// \param[in] doc The document that owns the AtomGoal
      	///
      	void Unsubscribe(C4DAtomGoal *observer, BaseDocument *doc);
      		
      	///
      	/// \brief Sends a messages to all subscribed observers
      	///
      	/// \param[in] type Message type
      	/// \param[in] doc The document that owns the subscribed observers
      	/// \param[in] data Optional message data
      	///
      	void Message(Int32 type, BaseDocument *doc, void *data = nullptr) const;
      
      private:
      	BaseLinkArray _observers;
      };
      
      maxon::Result<void> Observable::Subscribe(C4DAtomGoal *observer, BaseDocument *doc)
      {
      	if (!observer)
      		return maxon::NullptrError(MAXON_SOURCE_LOCATION, "Observer must not be nullptr!"_s);
      
      	// Check if this observer is already registered
      	const Int32 observerIndex = _observers.Find(observer, doc);
      	if (observerIndex != NOTOK)
      		return maxon::OK;
      
      	// Register new observer
      	if (!_observers.Append(observer))
      	{
      		return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION, "Failed to add observer to the list!"_s);
      	}
      
      	return maxon::OK;
      }
      
      void Observable::Unsubscribe(C4DAtomGoal *observer, BaseDocument *doc)
      {
      	if (observer && doc)
      	{
      		const Int32 observerIndex = _observers.Find(observer, doc);
      		if (observerIndex != NOTOK)
      		{
      			_observers.Remove(observerIndex);
      		}
      	}
      }
      
      void Observable::Message(Int32 type, BaseDocument *doc, void *data) const
      {
      	for (Int32 i = 0; i < _observers.GetCount(); ++i)
      	{
      		C4DAtomGoal *atom = _observers.GetIndex(i, doc);
      		if (atom)
      		{
      			atom->Message(type, data);
      		}
      	}
      }
      
      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Dynamic elements in a CYCLE, CYCLE empty after loading document?

      Hi Adam, happy new year to you, too!

      Since it works now, for some reason, I am pretty happy with what I have. However, since it might interest other plugin developers, I'll share more code. Maybe you have some tipps about improvements or potentially dangerous stuff, too.

      The idea is that the shader has a LINK field where the user can link an object (which is also part of my plugin). The object can (but doesn't have to) provide a list of "custom outputs" that will be added to the shader's CYCLE. In the screenshot below it's the "Difference Map".

      When a rendering is started, the shader will request the according data from the linked object during InitRender(). But that's not part of this thread 😉

      Screenshot 2021-01-26 at 11.42.45.png

      The shader's GetDDescription():

      Bool TerrainOperatorShader::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
      {
      	iferr_scope_handler
      	{
      		GePrint(err.GetMessage());
      		return false;
      	};
      
      	if (!description->LoadDescription(node->GetType()))
      		return false;
      	flags |= DESCFLAGS_DESC::LOADED;
      
      	BaseDocument* doc = node->GetDocument();
      	const BaseContainer& dataRef = static_cast<BaseShader*>(node)->GetDataInstanceRef();
      	
      	// Hide or show attributes, depending on shader mode
      	const Bool slopeMode = dataRef.GetInt32(XTERRAINOPERATORSHADER_DATA) == XTERRAINOPERATORSHADER_DATA_SLOPE;
      	TF4D::GUI::ShowDescription(node, description, XTERRAINOPERATORSHADER_SLOPE_DIRECTION_ENABLE, slopeMode);
      	TF4D::GUI::ShowDescription(node, description, XTERRAINOPERATORSHADER_SLOPE_DIRECTION, slopeMode);
      
      	// Get linked object
      	BaseObject *linkedObject = dataRef.GetObjectLink(XTERRAINOPERATORSHADER_OPERATORLINK, doc);
      	if (linkedObject)
      	{
      		// Get linked object's NodeData
      		TF4D::BaseTerrainOperatorData* linkedOperator = linkedObject->GetNodeData<TF4D::BaseTerrainOperatorData>();
      
      		// Get list of custom outputs (these are the elements to add to the CYCLE)
      		maxon::BaseArray<TF4D::GUI::CycleElementData> customOutputs;
      		if (linkedOperator->GetCustomOperatorOutputs(customOutputs))
      		{
      			if (!TF4D::GUI::AddCycleElements(node, description, XTERRAINOPERATORSHADER_DATA, customOutputs, true))
      				iferr_throw(maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not add LONG CYCLE elements!"_s));
      		}
      	}
      
      	return SUPER::GetDDescription(node, description, flags);
      }
      

      The linked object's NodeData's GetCustomOperatorOutputs():

      Bool ErosionOperator::GetCustomOperatorOutputs(maxon::BaseArray<TF4D::GUI::CycleElementData>& customOperatorOutputs) const
      {
      	iferr_scope_handler
      	{
      		GePrint(err.GetMessage());
      		return false;
      	};
      
      	customOperatorOutputs.Reset();
      
      	// GetCustomOutputName() simply returns a maxon::String
      	customOperatorOutputs.Append(TF4D::GUI::CycleElementData(TF4D_CUSTOMOUTPUT_EROSION_DIFFERENCE, GetCustomOutputName(TF4D_CUSTOMOUTPUT_EROSION_DIFFERENCE))) iferr_return;
      
      	return true;
      }
      

      Cheers,
      Frank

      Ah, damn. Now I've spoiled that I'm working on erosion for Terraform4D 😄

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: PySide2 Integration

      Why don't you avoid all those difficulties and simply implement that dialog using the C4D UI?

      posted in General Talk
      fwilleke80F
      fwilleke80
    • RE: Save node data in Read and Write

      If you're storing data in the tag's BaseContainer, you don't need to override Write() and Read(). That's meant for private class member whose data would be lost otherwise.

      Also, you can remove the "return true" at the end of the functions. As you already return in the previous line, it will never be called.

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Type Viewer not working in Visual Studio 2015

      I did today, including it in projectdefinition.txt worked smoothly, thank you!
      If I ever find out why copying it do the folders does not work, I'll post it here.

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Threading & job questions

      Oh my god. the stupidest error.... 😵
      OK, question 1 solved already, thanks 😁

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Creating and saving a 32bit grayscale bitmap

      Ah, great, thank for checking!
      Then I don't need to worry about my bmp code 🙂

      Cheers,
      Frank

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80

    Latest posts made by fwilleke80

    • RE: How to prevent handle drawing

      Ha, right, I should've thought of that ^_^
      Thank you, it works!

      Cheers,
      Frank

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • How to prevent handle drawing

      Hi,

      just out of interest: In an object plugin which uses the GetHandleCount() / GetHandle() / SetHandle() functions, is there a way to prevent C4D from drawing the standard handles? I want to draw my own handles, but the yellow standard handles are still visible behind mine.
      Skipping the call to ObjectData::Draw() in my object's Draw() function doesn't seem to be a good option, as that also skips drawing of the object gizmo.

      Cheers,
      Frank

      posted in Cinema 4D SDK c++
      fwilleke80F
      fwilleke80
    • BaseContainer::GetDirty() doesn't always change in C4D 2024

      Hi,

      I noticed that, in C4D 2024, the return value of BaseContainer::GetDirty() does not always change after changing a value in the container. Is this a bug? I am using BaseContainer::GetDirty() to detect changes to a container, and now this doesn't work anymore.

      How can I detect changes to a BaseContainer in C4D 2024?

      If a fairy granted me three wishes, I would greatly appreciate the addition of functions like BaseContainer::GetHashCode(), GeData::GetHashCode(), and a standard way to implement a GetHashCode() function for CustomDataTypes (which would be invoked by GeData::GetHashCode()).

      Cheers,
      Frank

      posted in Cinema 4D SDK 2024 c++
      fwilleke80F
      fwilleke80
    • RE: BaseContainer::GetCustomDataTypeWritableObsolete()... any side effects?

      Hi Ferdinand, thank you for the clarification!

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: BaseContainer::GetCustomDataTypeWritableObsolete()... any side effects?

      Hi Ferdinand,
      happy new year! Thanks for chiming in! I have fixed the crash in the meantime (don't ask how, it was all quite confusing, but it might have had to do with how I got the BaseContainer in the first place.

      So, if I understand this correctly, most of the structures in C4D 2024 that use COW will create a new instance when getting a writable reference or pointer, while getting a const ref or pointer will use a shared instance? And BaseContainer::GetCustomDataTypeWritableObsolete() will not do that, but still allow me to modify the pointed data? I can live with that 🙂

      Posting code is difficult, as everything is part of a pretty large project. I'm sorry my questions are sometimes a bit shadowy.

      Cheers,
      Frank

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: BaseContainer::GetCustomDataTypeWritableObsolete()... any side effects?

      I am not modifying any data from within GetVirtualObjects(), of course not.

      But Tilo's comment in the code sounds like the data is modified anyway, just by calling GetCustomDataTypeWritableObsolete(). "needs to be rewritten for COW support because this modifies data inside the container data" sounds pretty clear, that's why I'm asking.

      Cheers,
      Frank

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: BaseContainer::GetCustomDataTypeWritableObsolete()... any side effects?

      Any news on this? 🙂

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: VolumeData::AttachVolumeDataFake() in latest C4D 2024 API

      Super, thank you!

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • RE: Console output: Description::SetPDescription param(-1) is invalid?

      Ok, thanks! 🙂

      posted in Cinema 4D SDK
      fwilleke80F
      fwilleke80
    • Console output: Description::SetPDescription param(-1) is invalid?

      Hello,
      my third post this morning 😄

      In the latest C4D 2024, I frequently see this in the Visual Studio debug console:

      Description::SetPDescription param(-1) is invalid
      

      This message is definitely not sent from my code. And having searched all API code and the SDK docs, I can find no trace of anything called SetPDescription. And I don't think I'm ever passing a description ID of -1 anywhere. What is it trying to tell me?

      Cheers,
      Frank

      posted in Cinema 4D SDK 2024 c++ windows
      fwilleke80F
      fwilleke80