Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
Senior Software Developer
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); }
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.
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:
This is the render setting in my example scene (which just contains a Cube):
Here is the render result in the Picture Viewer, notice the correct file name:
Uploading and rendering the file on TeamRender produces this:
The debugger clearly shows that doc->GetFirstObject() returns nullptr.
doc->GetFirstObject()
nullptr
Cheers, Frank
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); } } }
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".
LINK
CYCLE
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
InitRender()
The shader's GetDDescription():
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():
NodeData
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; }
Ah, damn. Now I've spoiled that I'm working on erosion for Terraform4D
Why don't you avoid all those difficulties and simply implement that dialog using the C4D UI?
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.
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.
Oh my god. the stupidest error.... OK, question 1 solved already, thanks
Ah, great, thank for checking! Then I don't need to worry about my bmp code
Okay, thanks!
Cool! Thanks for your community work, Kent!
Oh, ok. I assumed he just wanted to set the document min time and didn't know a better way to get a BaseTime than using StringToNumber.
Since this question is tagged as C++, I'll add the docs link to the dedicated functions that return and set this attribute: BaseObject::GetDeformMode() and BaseObject::SetDeformMode()
If you simply want to set the document's min time, why not just do this:
doc[c4d.DOCUMENT_MINTIME] = c4d.BaseTime(11)
@m_adam said in macOS Monterey - Can't run source processor anymore?:
So you have to link for example from /usr/local/bin to a python installation, I personally have Python 3.8.9. Xcode itself ships with a python version you could link to (its in Contents/Developer/usr/bin of the application package). So with that path you can call call sudo ln -s PATH_TO_PYTHON /usr/local/bin/python in terminal.
Hi Adam!
That is in deed what I tried, creating a symlink that points to Python 3. I didn't use the paths, though, but just linked the word "python" to "python3". I guess that was my mistake.
Wouldn't it be easier for Maxon and everybody else, if you just changed the preprocessor call in the projects' PreBuild events to "python3", instead of having to go the symlink way and change things on users' computers (which, I guess, will have side effects when users later decide to install Python 2.7 for some reason)?
Hi Ferdinand,
Thanks! And no problem about delayed reply, I remember how it is with company meetings the problem is resolved now, that’s what counts.
Ok, just for a test I reinstalled Python 2.7.18 today, and voilà! everything works again. I must say, I find it weird that the Maxon source processor relies on a Python version that has been discontinued since 1st Jan. 2020. It's probably about time to update it.
I‘m indeed on macOS 12.3.1.
Can’t really say when exactly the problem started, as this is normally not my development machine.
Hi Kent,
Thanks for your reply! I did not change anything on that machine. Project Tool has long been working, and so were the builds. I have no idea why it doesn’t work anymore, my only suspicion is that something got auto-updated. Strange…