Why won't my python generator update? R2024
-
I'm using user data to change rotations in a python generator. But it doesn't seem to want to update when I play the timeline? Should I add anything special to the script?
from typing import Optional import c4d from c4d import utils doc: c4d.documents.BaseDocument # The document evaluating this python generator op: c4d.BaseObject # The python generator hh: Optional["PyCapsule"] # A HierarchyHelp object, only defined when main is executed distance = op[c4d.ID_USERDATA,1] offset = op[c4d.ID_USERDATA,2] rotation = op[c4d.ID_USERDATA,3] def main() -> c4d.BaseObject: # create hierarchy of nulls points = 100 points_list = [] for p in range(points): pt = c4d.BaseObject(c4d.Onull) pt.SetParameter(c4d.NULLOBJECT_DISPLAY,c4d.NULLOBJECT_DISPLAY_SPHERE,c4d.DESCFLAGS_SET_NONE) if p==0: points_list.append(pt) else: pt.InsertUnder(points_list[p-1]) points_list.append(pt) # adjust position and rotation for i,pt in enumerate(points_list[::-1]): off = c4d.Vector(distance * i,0,0 ) mg = pt.GetMg() mg.off = off print(rotation.x) rm = c4d.utils.HPBToMatrix(rotation) pt.SetMg(mg * rm) return points_list[0]
-
Hi @SweepingMotion,
There's no time dependency in the code.
Most probably you're trying to animate with parameters, so you need to put user data getters under the
main()
function.distance = op[c4d.ID_USERDATA,1] offset = op[c4d.ID_USERDATA,2] rotation = op[c4d.ID_USERDATA,3]
In some specific cases it's also required to uncheck "Optimize Cache" checkbox.
-
Hello @SweepingMotion ,
Welcome to the Plugin Café forum and the Cinema 4D development community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our Forum and Support Guidelines, as they line out details about the Maxon SDK Group support procedures. Of special importance are:
- Support Procedures: Scope of Support: Lines out the things we will do and what we will not do.
- Support Procedures: Confidential Data: Most questions should be accompanied by code but code cannot always be shared publicly. This section explains how to share code confidentially with Maxon.
- Forum Structure and Features: Lines out how the forum works.
- Structure of a Question: Lines out how to ask a good technical question. It is not mandatory to follow this exactly, but you should follow the idea of keeping things short and mentioning your primary question in a clear manner.
About your First Question
There are two layers to your question so to speak. Let us start with the outer more abstract layer.
What you are trying to do here, have the object update every time the time changes, is not an intended functionality of generator objects. And while you can achieve this more or less directly, this will be quite costly. It is fine when you do this for a couple of objects which you do not ship as plugins, but for everything else, you should do this properly. Th best way would be to expose an animatable parameter Time on the object, akin to what the Explosion deformer does for example. Updates of generator caches should always be tied to data changes on scene elements, either the cache holding element itself, or one of its inputs (things being "dirty" as Cinema calls it).
The second layer to this is object dirtiness and evaluating when to build the cache. You have probably ticked Optimize Cache in your generator, which will cause it only to update when its data container is dirty. In the video below I demonstrate the effect. We also have talked about the subject of evaluating dirtiness multiple times, a good starting point could be this answer of mine.
Fig. I: Unticking the cache optimization will cause the generator cache to react to time changes. But that is only an indirect effect, as unticking that option means that your cache is build every time Cinema 4D asks a scene to update itself (which can happen very often).
And while as indicated above you can take the performance penalty of just always building your cache, you should consider either the parameter route as suggested by @baca or done by the explosion deformer, or build yourself a dirty condition which checks if the time has changed (or the data container is dirty) since the last time you build the cache (see the link from above for details).
Cheers,
FerdinandCode:
"""Returns a cube object that is rotated based on the time of the document it is contained in. I used this script in the video above. """ import c4d op: c4d.BaseObject # The Python generator object. def main() -> c4d.BaseObject: cube: c4d.BaseObject = c4d.BaseObject(c4d.Ocube) if not cube: return c4d.BaseObject(c4d.Onull) t: float = op.GetDocument().GetTime().Get() cube.SetMg(c4d.utils.MatrixRotX(t)) return cube
-
@ferdinand I think issue is only with user data variables, because those are not being updated during playback, once they are out of main() function
Just a guess, because there's no sample project.
It probably should be like so:from typing import Optional import c4d from c4d import utils doc: c4d.documents.BaseDocument # The document evaluating this python generator op: c4d.BaseObject # The python generator hh: Optional["PyCapsule"] # A HierarchyHelp object, only defined when main is executed def main() -> c4d.BaseObject: distance = op[c4d.ID_USERDATA,1] offset = op[c4d.ID_USERDATA,2] rotation = op[c4d.ID_USERDATA,3] # create hierarchy of nulls points = 100 points_list = [] for p in range(points): pt = c4d.BaseObject(c4d.Onull) pt.SetParameter(c4d.NULLOBJECT_DISPLAY,c4d.NULLOBJECT_DISPLAY_SPHERE,c4d.DESCFLAGS_SET_NONE) if p==0: points_list.append(pt) else: pt.InsertUnder(points_list[p-1]) points_list.append(pt) # adjust position and rotation for i,pt in enumerate(points_list[::-1]): off = c4d.Vector(distance * i,0,0 ) mg = pt.GetMg() mg.off = off print(rotation.x) rm = c4d.utils.HPBToMatrix(rotation) pt.SetMg(mg * rm) return points_list[0]
-
I i_mazlov referenced this topic on Nov 8, 2023, 4:38 PM
-
@baca @ferdinand Thank you both.
It was the fact that the user data was not collected in the main function. Silly I didn't catch that.
It's working fine now.