Idiomatic way to populate text objects in Cinema 4D using the C++ SDK
-
Hi,
I am posting in this forum as I do not have a specific question about any API in C4D, rather I need to know the right way to do something. Of course, if this does belong in the C++ SDK forum, please move it - sorry for causing extra work!I am working on a plugin - realized as an object data generator - that loads some data from an external source. After parsing and cleaning this data, I want to put it in text objects (splines or full 3D text objects) appointed by the user. I.e. the user should be able to say "I want text string number 1 from The Plugin to be put in Text Spline 1". There would be multiple text strings, and multiple text splines/objects to be controlled by the plugin. There are also some parameters in the plugin that could cause the text strings to change, for instance over time in an animation.
What would be a good way to do this in Cinema 4D? I have thought of two possibilities so far, but since I am relatively new to the SDK, I wanted to check if I am going in the right direction, or if there is a better way.
First option: I create link fields in my plugin parameters. The user drags in the text spline/object he wants to contain the given text field data. I would need another parameter - like a dropdown - to select which text string gets populated to this particular text object.
Second option: I create a tag, applied to the text spline/object, that links to my plugin instance and a text string it contains.
The goal would be to have these text objects change their content immediately in response to whatever happens in my plugin. If my plugin - becasue of the animation time, a keyframe, or some other user configured thing - changes the text strings in the data set, the text object needs to change immediately. I.e. text objects that I did not create need to be updated when something changes in my object.
As an example, say there are two text objects in the scene. One called "Region" and one called "Sales quota". The data in my plugin has two regions, one "EMEA", one "Americas", and two sales quota values, say "100k" and "150k". There is one input parameter that decides if we are currently showing value 1 or value 2. When the user changes this parameter, the text objects should immediately update their content accordingly.
I don't know how to explain this better, so I hope it is possible to understand it. If there are any questions, please let me know. And if you know how I can programmatically set this up without having to generate Xpresso setups that would be awesome.
Thanks!
-
-
Hello @Havremunken,
thank you for reaching out to us. I would say your question is clearly API related and also concrete, as you have a specific case. This is why I have moved your posting to the Cinema 4D SDK forum. There is also no need to apologize, as long as users are not mindlessly posting off-topic questions (which very rarely happens) we do not mind moving things from time to time.
Examples for questions/topics that at least I would consider off-topic are:
- 'What to look out for when getting started with the Cinema 4D API?'
- 'How to use the BOOST library with the Cinema 4D SDK?'
- 'Best Practices for writing NodeData plugins?'
So, you should not ask broad questions in the SDK sub-forums which more aim at having a discussion (1 & 3) than finding out facts or ask about things not shipped by Maxon (2). The example (3) is also right on the edge, and some might claim it is a valid SDK question.
About your Question
I think it is good that you ask, because there indeed are some cliffs here. Because the topic is complex, let us introduce some terms. There is a data provider (DP) in your project, the thing which loads and provides string data, and there are data consumers (DC) which are fed by the DP with data, for example the text objects you mention.
- How you move your data, if you (1) push from the DP to the DCs (the link fields are in the DP) or if you (2) pull data from the DP to the DC (link field(s) is in the DC) has in principle little technical consequence.
- But option one might be more convenient for the user as he/she then has all links in one place, instead of having to manage multiple entities as in (2).
- In option two you will likely also need a third entity, a tag, as pointed out by yourself, to add the link fields to the DC. You could also work with adding user data fields to your actual DCs, the text objects, but in general option two will be more work/more complicated I would say.
- What you did not talk about, at least not explicitly, is the plugin type of your DP. You mention an 'object data generator', but it is unclear to me if this actually refers to the DP. There are two options here:
- The DP is a node itself, e.g., a
TagData
plugin, or as you somewhat implied anObjectData
plugin. While this is certainly desirable from a user perspective, everything integrates into the Object Manager and you do not have manager bloat, this approach comes with its hurdles. Because the 'node that manipulates the scene it is contained in'-pattern is tricky and lead to threading and update problems, as we talked about in one of your prior topics. In general nodes are not meant to change the scene while they are executed, as this simply leads to race conditions and crashes.- The exception to this are tags, which are executed at a specific stage and are allowed to change parameters and things like the transform of an object. But also tags are not allowed to add or remove other nodes from the scene when they are executed or do things like changing the point count of something. Effectively you are not allowed to allocate or deallocate things on scene execution, because pointers might go out of whack otherwise.
- What one can always do is use any of the node types just as a husk, ignoring their main execution methods such as
TagData::Execute
orObjectData::GetVirtualPbjects
, and setup shop in the usually on the main thread runningNodeData::Message
so that we then can change the scene to our hearts content.
- The other option would be your DP being a
CommandData
plugin with aGeDialog
as its own manager. The advantage here is that you are not bound by the scene execution loop, in with that do not have to worry about threading contexts. But you still might want to and can use threading yourself for expensive things to not pollute the main thread you are on here by default. Also update dependencies are easier to solve here, as you can just change the parameters of objects, optionally flag things as dirty, and then callEventAdd
. InNodeData::Message
you can also do most of this, but there we must be careful withEventAdd
. Because despite being on the main thread,EventAdd
can lead to nasty feedback loops inNodeData::Message
.
- The DP is a node itself, e.g., a
Conclusion
The Data Provider - Data Consumer pattern is a complex problem for scene elements (
NodeData
) and there are no easy answers here.- In general, I would always recommend the easiest approach. Which would be here a
CommandData
plugin with aGeDialog
with link boxes and other inputs which simply modifies the scene after the user has made an interaction and then pushes an event. This is how this pattern was meant to be done. - But the allure of
NodeData
has pushedCommandData
a bit to the side over time. As often it can be desirable to not have your own manager and instead integrate into the Object and Attribute Managers. Here you can face issues with threading and propagating updates (without shooting yourself in the foot). But this is certainly doable, we, Maxon, and third parties such as Insydium with X-Particles, do it all the time. It just takes a bit more experience.
Cheers,
Ferdinand -
Hi Ferdinand,
Once again, many thanks for your insightful answer!
One followup question that might affect the decision on how to implement this - CommandData plugin vs. NodeData-based - is what if the user wants two or more sets of data in the scene?
Imagine we have sales data from one quarter in one data file, and similar data from the same quarter a different year in another file. We want this on screen to show the differences and the similarities. Of course you could edit the data into one file, but I am trying to make things trivial for a potential user here. This would mean two separate sources of data (DP in your terms) "controlling" two different sets of text objects (DCs) in the same document.
Would this tip the scales in favour of either approach? Of course I want as little problems with threading as possible, and all I really need to do is make sure the text property of the DC object is in "sync" with the data field of the DP. I was hoping this would be doable with a minimum possibility of wreaking havoc in Cinema 4D land.
Thanks again!
-
Hey @Havremunken,
Just so that I understand you correctly, in the previous case one could have said that each data consumer had exactly one provider and that providers had zero to many consumers, i.e., the DP-DC relation was 1:N. You now want to change this to N:M, i.e., there can be now one to many providers for each consumer.
I do not really see a scenario where this could cause more problems in the
NodeData
case, but I could be overlooking something. TheCommandData
case is (almost) indestructible, so we really do not care there. But yes, when push comes to shove, this pushes the scales a bit in favor ofCommandData
, because N:M is always nasty andNodeData
is not a good starting point for nasty in this context. But I would still say it is doable, I would however be reluctant to give guarantees, as this is such a complex problem that I would simply have to try myself to know for sure.Cheers,
Ferdinand -
Hi Ferdinand - I understand that is is complex and not something that you can easily give an answer to that would be correct in every situation.
For me the use case for this is 1:N, but when I read your first reply, it hit me that it could actually theoretically be useful with N:M too. At least 2:N, I doubt much more than that, but programming wise "two is many" - i.e. more than one, so programming for 2 and 1024 won't be much different, but if was only ever going to be 1 DP, that would make a difference.
I am leaning towards CommandData like you indicate, but I will experiment a bit with the code to actually set the text content parameter. I also have some other calculations that need to calculate based on for instance the bounding box of the text object after the text has changed, and I want to do this without ending up in some sort of document destroying hell. Don't be surprised if more questions show up eventually if I run into race conditions after I paint myself into a corner.
Thanks again!