instance objects onto points with arbitrary attributes (python)
-
I have a list of thousands of points where I'd like to add a brightness value to each of them to drive material emission and instance spheres onto the points. I've been reading posts like this and this--how would you approach this type of task in python?
I can setup a cloner object in multi instance mode but I'm not clear if it's possible to pass the brightness values to the (invisible) motagdata tag... Would a python generator be a better way to go?
-
Hello @tom_01,
Thank you for reaching out to us.
Would a python generator be a better way to go?
It depends a bit on how this question is meant. In general, it is not advisable to try to recreate a MoGraph cloner implementation from scratch in Python, as the result would likely not be very fast. But if you want to just write a thin wrapper, i.e., a generator which uses a MoGraph cloner in its output to simplify a specific setup, there is nothing which prevents you from doing this.
The rest of your question is unfortunately a bit ambiguous. It is unclear to me:
- If the 'add a brightness value to each [vertex]' part is already done. You should store that data in a vertex map and I will not consider the generation of that data of the problem.
- If 'to drive material emission' means driving the emission of a material assigned to the polygon object (you could just use the vertex map here).
- Or if 'to drive material emission' means the spheres you want to clone. I.e., that you want to assign a material with an emission equal to the brightness data attached to the vertex the sphere is being cloned onto.
If you want to go route 2, I do not really understand what the question is. If you want to go route 3, this can be almost entirely done with out of the box MoGraph.
- Create a MoGraph cloner called Cloner.
- Create a clone called Sphere and clone target called Target.
- Parent Sphere to Cloner
- In Cloner:
a. Set Mode to Object.
b. Set Object to Target.
c. Set Distribution to Vertex. - Create a material called Material.
- In Material:
a. Disable all channels except Luminance.
b. Add a MoGraph Color Shader to the Luminance texture channel.
c. Set the the Color Shader to Channel: Color.
- Add a random effector called Random to the setup.
- In Random in the tab Parameter:
a. Set Color Mode to Custom Color
b. Enable Use Alpha/Strength.
Which will give you a set of Sphere clones cloned onto Target where the luminance of each clone is driven by the effector Random. You now just have to replace steps 7 and 8, the random effector, with a custom Python effector setup which sets the color of a clone depending on your data. Find an example which does that at the end of this posting.
Cheers,
FerdinandThe result:
The file: vertexmap_clones.c4d
The code:"""Example for driving the color of clones with a vertex map. """ import c4d op: c4d.BaseObject # The python effector def main() -> bool: """Executed by Cinema 4D to evaluate the clones of the cloner. This code assumes the Python effector to be in "Full Control" mode. """ # Get the MoData from the cloner and and the vertex map which has been linked in the user data # of the Python effector. moData: c4d.modules.mograph.MoData = c4d.modules.mograph.GeGetMoData(op) if moData is None: return False vertexMap: c4d.VariableTag = op[c4d.ID_USERDATA, 1] if not isinstance(vertexMap, c4d.VariableTag): return False # Get the data, the weights, from the vertex map and bail if they do not match the number of # clones. weights: list[float] = vertexMap.GetAllHighlevelData() if len(weights) != moData.GetCount(): return False # We now simply write the weights as an array of vectors. This works because in this setup the # clones are cloned onto the same mesh for which the vertex map has been generated for. The # vertex map and clones therefore share an index order, i.e., both the n-th weight and clone # will have been constructed for the same vertex. # # If necessary, this relation could also be given up (to for example use vertex maps/input data # which has not been generated for the same mesh), but then one would have to perform closest # element lookups which are quite expensive to do. colors: list[c4d.Vector] = [c4d.Vector(w, w, w) for w in weights] # Write the data as the color array into the clone data. moData.SetArray(c4d.MODATA_COLOR, colors, False) return True
-
Hi Ferdinand. Thank you for the thorough response. Apologies for the ambiguity on my part.
For a test project I wanted to fetch star coordinates (for ~100K–1M+ stars) and their brightness values. Then instance Redshift spheres onto them with a material where the brightness of each point is piped into the material's emission.
This was a test project to get started with python. I also wanted to see if c4d has a way of working with points and attributes. I don't think the real-world (galaxy?) data is available, but ideally each star would also have a radius, which would drive the sphere scales/sizes.
I'm familiar with cloner/effector/field setups, but I was looking for a programatic approach. I don't know how to pass specific values to each point (brightness in this case) and then to clones with effectors/fields.