Navigation

    • Register
    • Login
    • Search
    1. Home
    2. fwilleke80
    3. Best
    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups

    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 •
    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 •
    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 •
    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 •
    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 :D

    posted in Cinema 4D SDK •
    RE: PySide2 Integration

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

    posted in General Talk •
    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 •
    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 •
    RE: Threading & job questions

    Oh my god. the stupidest error.... :dizzy_face:
    OK, question 1 solved already, thanks :grin:

    posted in Cinema 4D SDK •
    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 •
    RE: Python Source Protector: Can it be called via CLI?

    Sounds like a good solution to me, too!
    And I second Kent's question about the compatibility of encryptet pyp files.

    posted in Cinema 4D SDK •
    RE: Overlaying icons (like Alembic)

    OK, I got it. Once it's all figured out, the solution seems laughably simple.

    In case anybody else needs to maintain R20 compatibility and wants to use custom icon overlays, here's a solution:

    In MyObject class declaration:
    (R20 code)

    class MyObject : public ObjectData
    {
    	INSTANCEOF(MyObject, ObjectData);
    
    	// ...
    
    private:
    #if API_VERSION < 21000
    	BaseBitmap *_customIcon; ///< Used to store a node's custom icon in R20
    #endif
    };
    
    public:
    #if API_VERSION < 21000
    	MyObject() : _customIcon(nullptr)
    	{
    	}
    
    	~ MyObject()
    	{
    		if (_customIcon)
    			BaseBitmap::Free(_customIcon);
    	}
    #endif
    

    In MyObject::Message():
    (R20 code)

    const BaseContainer &dataRef = op->GetDataInstanceRef();
    
    //
    // 1. Copy default icon into customIcon
    //
    
    // Load default icon
    IconData defaultIconData;
    if (!GetIcon(node->GetType(), &defaultIconData))
    	return false;
    
    // Free _customIcon if it has been allocated before, because
    // it will now receive the pointer to a newly allocated BaseBitmap.
    if (_customIcon)
    	BaseBitmap::Free(_customIcon);
    
    // Get the actual bitmap that is our icon
    _customIcon = defaultIconData.GetClonePart();
    if (!_customIcon)
    	return false;
    
    //
    // 2. Blit overlay into customIcon
    //
    
    // Load overlay icon
    IconData overlayIcon;
    const Int32 overlayIconId = MyFunctionToGetTheIconId();
    if (overlayIconId == NOTOK)
    	return false;
    const Bool overlayIconloaded = GetIcon(overlayIconId, &overlayIcon);
    if (overlayIconloaded)
    {
    	BlitOverlayBitmap(overlayIcon.bmp, _customIcon, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 32, 32);
    
    	//
    	// 3. Set cid->dat to use customIcon
    	//
    	cid->dat->bmp = _customIcon;
    	cid->dat->x = 0;
    	cid->dat->y = 0;
    	cid->dat->w = _customIcon->GetBw();
    	cid->dat->h = _customIcon->GetBh();
    	cid->filled = true;
    }
    

    And BlitOverlayBitmap(), a variation of your BlitOverlayIcon():

    void BlitOverlayBitmap(BaseBitmap *overlay, BaseBitmap *dest, Int32 x, Int32 y, Int32 bw, Int32 bh, Int32 xOffset, Int32 yOffset)
    {
    	if (!overlay || !dest)
    		return;
    
    	BaseBitmap *overlayAlpha = overlay->GetInternalChannel();
    	BaseBitmap *destAlpha = dest->GetInternalChannel();
    
    	const Int32 destW = dest->GetBw();
    	const Int32 destH = dest->GetBh();
    	if (bw > destW)
    		bw = destW;
    	if (bh + yOffset > destH)
    		bh = destH - yOffset;
    	if (bw + xOffset > destW)
    		bw = destW - xOffset;
    
    	UInt16 aa, a, rr, r, gg, g, bb, b;
    	aa = a = rr = r = gg = g = bb = b = 0xff;
    
    	for (Int32 py = 0; py < bh; py++)
    	{
    		for (Int32 px = 0; px < bw; px++)
    		{
    			// get color and alpha from overlay icon
    			overlay->GetPixel(x + px, y + py, &r, &g, &b);
    			overlay->GetAlphaPixel(overlayAlpha, x + px, y + py, &a);
    
    			// get color and alpha from background icon if available
    			dest->GetPixel(px + xOffset, py + yOffset, &rr, &gg, &bb);
    			if (destAlpha)
    			{
    				dest->GetAlphaPixel(destAlpha, px + xOffset, py + yOffset, &aa);
    			}
    
    			// blend overlay color against existing icon background color
    			Float blend = a / 255.0;
    			dest->SetPixel(px + xOffset, py + yOffset, (Int32)Blend(rr, r, blend), (Int32)Blend(gg, g, blend), (Int32)Blend(bb, b, blend));
    
    			// use only the overlay alpha if the opacity is higher to keep the original icon complete
    			// and only in case the background has an alpha at all
    			if (destAlpha && aa < a)
    			{
    				dest->SetAlphaPixel(destAlpha, px + xOffset, py + yOffset, a);
    			}
    		}
    	}
    }
    

    Thanks for patience & support!

    Cheers,
    Frank

    posted in Cinema 4D SDK •
    RE: Plugin compiled on macOS Catalina for R23, not working on Big Sur?

    Oh wait, got it by displaying the notarisation log. The binary had no timestamp.

    After googling a bit, I fixed it by adding --timestamp=http://timestamp.apple.com/ts01 to "Other Code Signing Flags", and it was notarised successfully! :-)

    Is --timestamp=http://timestamp.apple.com/ts01 a good choice for this flag? The SDK docs just say "include a secure timestamp with your code-signing signature;", which is not too much information ;-)

    posted in Cinema 4D SDK •
    RE: Get Spline Data from document->GetActiveObject();

    Actually, I would use static_cast<SplineObject*>(op) instead of ToSpline(op) which is defined as only (SplineObject*)(op).
    After the appropriate tests (op->GetInfo() & OBJECT_ISSPLINE, op->IsInstanceOf(Ospline), et cetera), of course.

    posted in Cinema 4D SDK •
    RE: Errors when recompiling R20 plugin to R21

    I don't know about the first one, but the second one is probably because you're not doing any error handling.

    Do something like this:

    myBaseArray.Append(plugin_id) iferr_ignore();
    

    or

    maxon::Result<void> SomeFunction()
    {
        myBaseArray.Append(plugin_id) iferr_return;
    }
    

    or

    void SomeFunction()
    {
        iferr_scope_handler
        {
            GePrint(err.GetMessage());
            return;
        };
        myBaseArray.Append(plugin_id) iferr_return;
    }
    

    or

    iferr (myBaseArray.Append(plugin_id))
    {
        GePrint(err.GetMessage());
    }
    

    Cheers,
    Frank

    posted in Cinema 4D SDK •
    Using CodeEditor_Open()

    Hello,

    I'm experimenting with an object that accepts Python code. Therefore, I also need an "Open Editor" button to open the Python code in the code editor dialog.

    I have found this old thread here:
    https://plugincafe.maxon.net/topic/10352/13847_how-to-pass-python-code-to-c4ds-editor-window/2?_=1618561938314

    However, the code shown there does not work anymore.

    SCRIPTMODE enum

    bc.SetInt32(CODEEDITOR_SETMODE, SCRIPTMODE_PYTHON);
    

    I guess this must nowadays be:

    bc.SetInt32(CODEEDITOR_SETMODE, (Int32)SCRIPTMODE::PYTHON);
    

    GePythonGIL

    case CODEEDITOR_SETSTRING:
        {
          GePythonGIL gil;
          data->SetString(PARAMETER_PYTHONSCRIPT, msg.GetString(CODEEDITOR_SETSTRING));
      
          res = true;
          break;
        }
    

    First of all, there is no GePythonGIL anymore, so the code does not compile. Is maxon::py::CPythonGil the correct replacement?

    Second, what is it needed for? It's declared, but never used in this scope.

    PythonLibrary

    It seems this was in lib_py.h, but the SDK docs mention in the "Changes in R23" chapter that it's deprecated now. So, what should be used instead?

    pylib.CheckSyntax()

    I can also not find anything about CheckSyntax() in the current SDK docs.

    Callback function signature

    Even with an empty callback function that doesn't do anything, the code does not compile.

    No matching function for call to 'CodeEditor_Open' it says.

    The API defines the callback like this:

    Bool CodeEditor_Open(BaseList2D* obj, const maxon::Delegate<GeData(BaseList2D* obj, const BaseContainer& msg)>& callback, const BaseContainer& bc = BaseContainer());
    

    So it seems that has also changed since the thread I mentioned.

    . . .

    Long story short, I would really appreciate an up-to-date example of how to use CodeEditor_Open() and everything that has to do with it.

    Thanks in advance!

    Greetings,
    Frank

    posted in Cinema 4D SDK •
    RE: Using CodeEditor_Open()

    Ah, ok. I didn't even know that the Python Generator supports a Message() function :D

    Yes, that's exactly what I would like to have! The user should be able to implement some functions, and get provided with some variables to work with (like in the Python Effector).

    posted in Cinema 4D SDK •
    RE: Using CodeEditor_Open()

    Your example, by the way, works fine! Thank you again for that! I'm currently trying to extend it to all my needs :-)

    Therefore, here's another question:

    What if I don't want to have a BaseObject as a result, but something different, e.g. a BaseArray with values? The user would implement several different functions, some of which return a simple Int32 value, others get a BaseArray<Float> and should work on the array values, possibly changing them.

    maxon::BaseArray<Float> valueArray;
    
    auto* res = _scope.PrivateInvoke("MyFunctionThatFillsAnArrayOfFloats"_s, helperStack, maxon::GetDataType<maxon::specialtype::SomethingThatWorksWithAnArrayOfFloats*>(), &args.ToBlock()) iferr_return;
    
    if (res == nullptr)
    	return maxon::NullptrError(MAXON_SOURCE_LOCATION, "PrivateInvoke() result is NULL!"_s);
    
    maxon::specialtype::SomethingThatWorksWithAnArrayOfFloats* pyRes = res->Get<maxon::specialtype::SomethingThatWorksWithAnArrayOfFloats*>().GetValue();
    if (pyRes == nullptr)
    	return maxon::NullptrError(MAXON_SOURCE_LOCATION, "SomethingThatWorksWithAnArrayOfFloats result is NULL!"_s);
    

    Is there a maxon::specialtypethat allows me to pass a maxon::BaseArray<Float>, or a custom struct {} or even a maxon::BaseArray<MyStruct>?

    Thanks again! :-)

    Cheers,
    Frank

    P.S.: The Python Field does something similar, it implements the Sample() function and passes all these neat custom data structures to it.

    posted in Cinema 4D SDK •
    RE: R25 - Compile plugin issue

    Hi Ferdinand,

    I agree, but the problem is that projecttool also does play an automated inhouse-tool role, as you might still be aware of, and there these additional files are likely not welcome.

    I remember very well. I just didn't want to spoil any details here, without anyone from the SDK team mentioning it first ;-)
    It does make sense in the Maxon context where one solution hosts a multitude of projects. Just for plugin developers, it usually results in having the Project Tool generate a nice project, but then having to build the solution manually, with all included framework projects, build dependencies (even between the framework projects), and references. That's error prone, generates more support noise for you, and annoys the plugin developers ;-)

    But I will try to find a place for this idea, which then would have to be an option of the tool on a per project basis.

    I think there could be a very easy solution (well, at least it sounds easy... things are rarely as easy as they look on the outside, I know): The projetdefinition.txt specifies what type of file should be created:

    For projects:

    // Type of project - can be [Lib;DLL;App]
    Type=DLL
    

    For solutions:

    Type=Solution
    

    So, why not just allow something like this:

    Type=DLL,Solution
    

    It could simply do two complete runs then, one for each element in the enumerated Type. It would first do a normal run for Type=DLL, and then another one for Type=Solution. That would just require Project Tool to split the Type string by comma, and then iterate over the split parts and do one complete run for each of them.

    That way, plugin developers could have a much improved workflow when creating new projects, and maxon developers wouldn't be bothered with unwanted files.

    Currently, I still check in all my plugin project files for all Cinema 4D releases I support, into the source management, because regenerating them on the fly with Project Tool (which is, as far as I remember, the original idea behind it) is just way too much work.

    Cheers,
    Frank

    posted in Cinema 4D SDK •
    RE: CustomGui and CustomDataType examples... not used anywhere?

    I've set it back to SOLVED again.

    Just a hint for anyone else who might run into a similar problem: Make sure your CustomDataTypeClass::Compare() impementation does not falsely return 0 under certain conditions.

    The SDK example goes the easy route: It even says in the comments: "this just compares the number of points ... a better implementation would also compare the values of the points". What would be a good way to compare arbitrary data like the maxon::BaseArray<Vector> from the SDK example, and assign the "1" or "-1" results?

    Sometimes, the data isn't less or greater, sometimes it's just different.

    Cheers,
    Frank

    posted in Cinema 4D SDK •