Need help with python field - infection
-
Hi I'm writing a field effector that will work like infection in xparticles and need some pointers.
The logic is:- Weights are added based on objects from an object list influencing a vertex unless the vertex is on an INFECTED or DEAD list
- If a vertex has weight of over 0 it's added to the INFECTED list.
- All vertices from the INFECTED list have their weights increased every frame.
- If a vertex weight exceeds some threshold it's added to the DEAD list - it cannot be influenced by the field anymore.
- Every infected vertex has a chance of infecting neighbouring vertices within a radius (unless they are already on the INFECTED or DEAD list)
My questions are:
- The most important one: does a way of doing it already exist. What I want to do is have balloons simulation that dynamically pop when encountering and object from a list.
- I understand that vertices are given in blocks. How would I efficiently iterate over all of them - is there an option to thread/multiprocess them? Does anyone know of any example code I can follow and learn?
- How do I get neighbours of a vertex when it's neighbours might be in a different block?
-
Hello @SweepingMotion,
Thank you for reaching out to us. Here also applies: Please have a look at our Support Procedures, especially regarding the sections and Support Topic Rules and Asking Questions.
- Does a way of doing what already exist? I do not know the infection modifier from X-Particles and your list is so technical that I have troubles understanding your goals as your start trying to explain an algorithm right away. But the general gist seems to be cellular simulation to have a spreading infection over particles. No, there is no such modifier in MoGraph.
- A field effector will never give you 'vertices in blocks'. You get passed MoGraph particle data in the form of an MoData instance which then holds arrays for color, matrix, weight and so on. Apart from the type name
FieldOutputBlock
which is needed when you manually sample a field, there is not much with blocks going on here. - You cannot, between MoGraph particles there is no structural information, they are not vertices attached to a mesh. When your MoGraph particles have been initialized from a mesh, e.g., by an effector in deformation mode, you could assume that the a particle index corresponds the a vertex index in the mesh and then use something like c4d.utils.Neighbor to get neighboring vertices or polygons for a vertex. But there is no guarantee that this assumption will always hold true (you would have to test a bit). I do not understand what you mean with "different blocks" in this context.
I'm writing a field effector
Are you writing code for a Python Effector or a Python field? They are not the same and there is no field effector to my knowledge. From the context I assume it is an effector. In addition to the links shown below, you could also have a look at the default code of the Python effector, as it has been made more beginner friendly in 2024. There is default code for both the Parameter and Full Control mode which lines out the differences.
is there an option to thread/multiprocess them?
No, Python is not capable of true parallelism due to its Global Interpreter Lock. See the introduction of c4d.threading for details. Python's own multiprocessing module is just starting multiple VMs and lets them communicate with pipes to circumvent the GIL (which does not work with Cinema 4D because the to be shared data is much more complex).
Conclusion
I would suggest starting with some simple effector code. While doing what you want to do, implement some kind of cellular simulation is possible with Python, it would be especially demanding using a Python effector. The main problem is that a Python effector is not meant to store temporal data (look back one iteration) so that it can run a simulation each time it is queried. You must also factor in that an effector might be sampled multiple times per what you would consider "one iteration". Unclear is then also where you want to store your infected state, since
Modata
is a fixed set of arrays, you cannot just add your own infection array. You could store things as weights, and then simply compute on the current state but this is all getting very speculative.To be clear: A lot of things are possible Python, and a clever developer can almost always make all things work in any language and under any condition. But implementing some fancy
O(NĀ²)
simulation thing is not something Python is good at. If you then want to tie this even into MoGraph, this becomes even more complicated. You are better off with C++ and writing a full blownEffectorData
plugin.Cheers,
FerdinandUseful links in the context of MoGraph are:
-
Thanks. I'm writing a python field.
What I want to do is have vertex map grow from certain points, but dynamically so I don't have to use freeze which from what I gather you can only set once.
I played with the python field and it indeed goes over the points in chunks of 400. This is where the block came from. It's called that in the input field manual in the SDK.
I'll see if the neighbour function works. -
Hey @SweepingMotion,
What you encountered in the other thread is a threading related issue introduced by the 2024.0 API changes that will be fixed by Maxime. Fields neither operate on points nor in chunks, the sampling blocks are a technical detail. And while you can make fields work as drivers for vertex maps, feeding back the vertex information into the field sampling will be very tricky.
If you want to do what you are planning to do there, I would recommend realizing at least a Python
TagData
plugin, as this is least barrier free route to do this (using C++ would be better). You could also just use a Python Programming tag but that is even more limited. There were the 2021 praxiswochen where I used a Python Programming tag to compute the local thickness of a mesh with ray casting (the slides are in German but the c4d files are in English):The example actually is somewhat real time performant when you do not get too cocky with the ray numbers and the points in your mesh. So, if you know what you are doing, you can bend the rules a bit. But what you want to do is in tendency so algorithmically complex that it won't fair well in Python. The Coburg Praxiswochen example also showcases how poorly such Python solutions perform once you crank up the numbers.
Below I have provided a rough draft of how you could approach what you want to do. I mostly ignored the whole infection logic, and more focused on getting the basics going. I would still recommend using C++.
Cheers,
FerdinandFile
Result
-
Thank you @ferdinand that is fantastic help.
I think I'm going to put it on ice till I learn how to do a c++ field. What you made it almost exactly what I want BUT, I don't want to set the growth seeds - places where the growth starts, but rather add them dynamically. Think a balloon that touches a needle and it starts tearing from there, because that's exactly what I want to do.
-
Hey @SweepingMotion,
first of all, I did not want to discourage you as such this would be only possible in C++. All I am saying here is that having points in a mesh which look up their neighbors and then do a possible iterative spreading algorithm per iteration sounds costly.
You can certainly do this in Python, you should just brace for really bad performance when you run this on complex meshes with complex settings. That I used the modelling command for the growing part was not only based in my laziness but also the fact that the modelling command will run in C++ nice and fast.
Your Problem
Regarding your problem, I think I understand what you want. You want a field to (persistently) paint a vertex map to use this as a seed to run your simulation; think of a paint brush (the field) on canvas and the paint then spreading (the simulation). First of all, fields are not really meant to be persistent in that manner as that would be required here. Or to use your example, you would want your ballon tearing to continue even when the field on the tip of the needle is not touching any ballon vertex anymore after the ballon started tearing appart.
Fields are also not meant to do these whole field space computations a simulation implies. I.e., you get the point
x
and the field sampler spit out values for its weight and vector. A field modifier layer would be better suited for running a simulation as they focus more on all values in a field. The problem is that modifiers do not do the sampling part for a transform with a falloff. So you would kind of need both.One could certainly do all this when motivated enough. You could for example simply precompute all values and then sample from a look-up table/cache when your field is sampled. But this all seems very complicated for no good reason.
Recommendation
- I would still recommend using at least a Python
TagData
plugin. C++ would be better, using a Programming tag also works, but you must be hackier, be more knowledgeable to make it work. - There are two ways how you could realize the field/brush influence thing you want:
- Ignore Mograph fields altogether. Here you would implement your own
ObjectData
plugin which does the painting. You would have two parts, the object(s) which are continuously painting sees into a vertex map, and your spider-in-the-net tag which then runs a simulation on that map. You would only need one vertex map in this case but would also have to reimplement falloffs and noise for the fields/brushes to the degree you need it. - You use Mograph fields but you do not mix them with the simulation directly. The idea is to have two vertex maps. One seed vertex map is driven by MoGraph fields and you can go ham there, use all of MoMgraph to your liking. Your plugin tag would then copy from that seed map into a simulation map on each iteration and advance the simulation. For example by either blending values or taking the max value of both maps. On that other map you then run the simulation. This would have the advantage that you have the temporal persistence for free (since you copy from a volatile seed map into the persistent simulation map). You would also side step the fact that you would have to somehow weave the simulation into fields. If you wanted then wanted to pretty this up, you could make it so that your tag has field list and for the user it looks like that there is only the one output vertex map while the tag uses internally a hidden vertex map into which it feed the field list which is part of its UI.
- Ignore Mograph fields altogether. Here you would implement your own
Number two is in my opinion the more sensible solution, number one would give you more freedom but would also be much more work.
Cheers,
Ferdinand - I would still recommend using at least a Python