New Child Render settings with python
-
Hi,
I am trying to generate a "new child" of a master render setting in python whilst trying to keep the values in that child to inherit from the parent, however I can only clone or copy the settings which make the values unique and I have to tell the child by hand afterwards to inherit the settings. Is there anyway to create a child in Python that will automatically inherit the masters settings? Here is my code, it generates a take as well and assigns a camera and render dimensions based on the objects name (i.e. Object_3840x2160):
import c4d def show_alert(message): dlg = c4d.gui.MessageDialog(message) return dlg def main(): # Get the active Cinema 4D document doc = c4d.documents.GetActiveDocument() take_data = doc.GetTakeData() # Retrieve information about the selected object sel_object = doc.GetActiveObject() object_name = sel_object.GetName() active_object = doc.GetActiveObject() # Check if the object name follows the correct format "NAME_WIDTHxHEIGHT" name_parts = object_name.split("_") if len(name_parts) != 2: show_alert("Please make sure the object's name follows the format: NAME_WIDTHxHEIGHT") return width_height_parts = name_parts[1].split("x") if len(width_height_parts) != 2: show_alert("Please make sure the object's name follows the format: NAME_WIDTHxHEIGHT") return try: width = int(width_height_parts[0]) height = int(width_height_parts[1]) except ValueError: show_alert("Please make sure WIDTH and HEIGHT are whole numbers in the format: NAME_WIDTHxHEIGHT") return def find_camera_child(obj): if not obj: return None for child in obj.GetChildren(): if child.GetType() == c4d.Ocamera: return child return None # Find a camera as a child of the active object camera_child = find_camera_child(active_object) if camera_child: print(f"Found camera child: {camera_child.GetName()}") else: print("No camera child found.") # Create a new take and set its properties new_take = take_data.AddTake("", None, take_data.GetMainTake().GetDown()) if camera_child: new_take.SetCamera(take_data, camera_child) take_name = name_parts[0] new_take.SetName(take_name) # Activate the newly created take and trigger an update take_data.SetCurrentTake(new_take) c4d.EventAdd() # Retrieve active render settings and create new ones active_render_settings = doc.GetActiveRenderData() #active_render_data = active_render_settings.GetClone parent_render_settings = active_render_settings.GetUp() or active_render_settings new_render_settings = active_render_settings.GetClone() new_render_settings_name = name_parts[0] new_render_settings.SetName(new_render_settings_name) # Set the resolution width and height in the new render settings new_render_settings[c4d.RDATA_XRES] = width new_render_settings[c4d.RDATA_YRES] = height print(f"Set resolution: {width}x{height}") # Insert the new render settings after the last sibling or as the first child last_sibling = parent_render_settings.GetDownLast() if last_sibling: new_render_settings.InsertAfter(last_sibling) else: new_render_settings.InsertUnder(parent_render_settings) # Set the new render settings as the active render data and trigger an update doc.SetActiveRenderData(new_render_settings) new_take.SetRenderData(take_data, new_render_settings) c4d.EventAdd(c4d.EVENT_FORCEREDRAW) if __name__ == "__main__": main()
-
I should mention also that the Object needs a camera as its child also. This setup is specific to a pipeline we are implementing in our studio.
Thanks
-
Hello @masterofthejack,
Although this is your second posting, it is your first thread, so let me welcome you 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
In short, this functionality is not public and works in general not too well with Python API as it assumes things to happen in a certain order (which the Python API can violate). The cloning behavior is determined by this setting of the parent you clone from:
When you clone from such parent, you will also clone this setting. It is expressed as a bitflag. The flag is not exposed in the public API (neither Python nor C++) because this is part of the backend behavior of a module. Internally it has the symbol
RENDERSETTINGS_BIT_OVERRIDEBEHAVIOUR
, and while one can set this flag and emulate other things, theInherit Override Behavior
option is tightly coupled to the Render Settings Menu and we cannot emulate everything it does.In practice this can mean that things go out of whack when you just toggle this setting often enough programmatically.
Cheers,
FerdinandResult:
You see there at the end that when I turn the behavior off for a node where we programmatically enabled it, the bitmask of that node goes out of whack, and things aren't anymore as they should be. -
Hi @ferdinand
Thanks so much for your comprehensive reply, very interesting. I couldnt emulate your result, as in the childs behaviour was still unique but I can see trying to do this will be problematic. As this script is built to generate a template to begin with I will just loop a step into our process which dictates to the creator to select all settings inside the children to inherit settings, one cannot completely omit manual tasks but one can try.One more problem Im having is camera that is apart of my setup (the camera that is the child of the object Im setting the dimensions with) doesnt update its frustrum or aspect ratio until I manually change the render dimensions in the newly created render settings slightly and return to the value created just to have it update in the veiwport, wonderingif there is some sort of update function I can include to do this.
I should mention that the camera has some xpresso attached that sets its frustrum to lock onto a plane regardless of its position, I developed it to behave much like an "nDisplay" from Unreal engine. Developing this script was integral for us to quickly set POV per projected space for the purpose of immersive projection content. Thank you for all your help so far, if I could share my setup and scene with you, it might make more sense but I understand this space is probably not meant for that.
-
Hello @masterofthejack,
One more problem I am having is camera that is a part of my setup [...] doesn't update its frustum or aspect ratio until I manually change [... the] settings slightly [...] I should mention that the camera has some Xpresso attached [...]
I have to speculate here a bit, since I do not fully understand what you are doing.
- When you change the render data of a document (or change anything else in it), this inherently will flag things as dirty and cause the scene passes to be executed, i.e., stuff to update.
- This however does not happen immediately when you run for example the line
rdata[c4d.RDATA_XRES] = 42
but when the main loop of Cinema 4D evaluates everything in a scene which is dirty. You can also trigger this yourself manually so that you can benefit early from changes (see below). c4d.EventAdd()
is a necessary component in Script Manager scripts, as things often seemingly 'won't update' . Things actually do update, but the UI does not immediately reflect that without anEventAdd
until the user then interacts with the scene (which causes anEventAdd
). I would however avoid usingEVENT_FORCEREDRAW
unless it is really necessary - which seems unlikely here.
What to do exactly, depends a bit on what you want to do and what your setup is. But in general you seem to run into the problem that the passes are not executed often enough (or at all) on your scene, what we talked about under (2.). You can do this with BaseDocument.ExecutePasses. It usage is quite straight forward, you just run it and things are then updated.
Cheers,
Ferdinand"""Demonstrates how to execute the passes on a document. """ import c4d from mxutils import CheckType doc: c4d.documents.BaseDocument # The active document def main() -> None: """ """ # Allocate a cube object and a look at camera tag on it. cube: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Ocube)) tag: c4d.BaseTag = CheckType(cube.MakeTag(c4d.Tlookatcamera)) doc.InsertObject(cube) c4d.EventAdd() # Meaningless here since the passes have not run #doc. # The cube has no cache at this point, and also the tag did not orient the cube yet. print(f"{cube.GetCache() = }") print(f"{cube.GetMg() = }") # Now we execute the passed on doc, specifically the cache and expression (i.e., tags) pass. doc.ExecutePasses(bt=None, animation=False, expressions=True, caches=True, flags=c4d.BUILDFLAGS_NONE) # Now things are "updated". print(f"{cube.GetCache() = }") print(f"{cube.GetMg() = }") # What we did here will automatically happen once a Script Manager script ended. There are two # scenarios where one has to execute the passes manually. # # 1. We want to make in code use of an updated scene before Cinema 4D on its own executes the # passes on it to get hold of information such as the cache or matrix here. # 2. Sometimes complex dependencies between scene elements can require passes to be executed # more than once when we build or update such setups. Three executions in a row should # suffice even for the most complex setups. # We have a field driven by a tag which is dependent on particle information. We change something # deep down in the dependency relation and want things to update all at once. Note that it is # NOT dependencies == number of executions. Internally, Cinema 4D executes the passes three # times in a row at most. Executing the passes is expensive. # This should wiggle pretty much everything into place. for _ in range(3): doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_NONE) if __name__ == '__main__': main()
-
Thank you @ferdinand, I'll need to work out how I implement but once again a thorough and considered reply....
-
-
-