Multiple unique render files from same file?
-
This concept of rendering multiple unique render paths was touched on here: https://developers.maxon.net/forum/topic/13061/adding-multiple-cameras-from-a-single-file-to-the-render-queue/22
The solution works pretty well but duplicates the source file multiple times.
My goal is to have a workflow where multiple objects in the same file can be rendered with their object name as their own jpeg. I am using an In/Ex User data and python generator to achieve this. The problem is the jpegs get overwritten each time.
I am trying to figure out a way to add a batch render element that waits until the render completes then adds a new file with the next object iteration.I am posting my scene file and python here. MultiSavePath_01.c4d
import c4d from c4d import gui, storage import os import fileinput #The goal is to render multiple files with unique render paths from this one file. I have this code in a python generator #with and in/ex user data, the code iterates through in/ex updating the render path and starting the render. The problem #is the render path and jpeg get overwritten to the same file. How do I render four separete jpeg files named Cylinder, Sphere, Cube and Figure? #thank you for any help! def message(id,data) : #Button listener if id == c4d.MSG_DESCRIPTION_CHECKUPDATE: if data["descid"][1].dtype == c4d.DTYPE_BUTTON or c4d.DTYPE_LONG: buttonID = data["descid"][1].id if buttonID == 2: SendShotsToRender() #SEND TO RENDER def main(): return def SendShotsToRender(): print("Send Shots To Render") doc = c4d.documents.GetActiveDocument() br = c4d.documents.GetBatchRender() rd = doc.GetActiveRenderData() br.Open() #Open the render queue window c4d.CallCommand(465003519) # Clear render queue of old render files inEx = op[c4d.ID_USERDATA,4] # In/Exclude User Data inExCount = inEx.GetObjectCount() #how many shots in our In/Exclude shot = op[c4d.ID_USERDATA,1] #The currently active shot for i in range(0,inExCount): #Iterate through each shot based on our shot slider in user data activeObj = inEx.ObjectFromIndex(doc, i) #deterimine which object is active render_filename = inEx.ObjectFromIndex(doc, i).GetName() #Gets the name of the currently active object rd[c4d.RDATA_PATH] = render_filename #Set the render files name source_file = doc.GetDocumentPath() + "/" + doc.GetDocumentName() #get the source document path br.AddFile(source_file,0) #Add the source file br.SetRendering(c4d.BR_START) #start rendering #print(br.GetElementStatus(c4d.RM_FINISHED)) #Is there a way I can wait for the render to finish then delete the file update the path and add a new one? #br.DelFile(source_file) if shot != i: activeObj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = True #turn visibility On activeObj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = True else: activeObj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = False #turn visibility Off activeObj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = False print(i,render_filename) op[c4d.ID_USERDATA,4] = inEx #write back to the IN/EXCLUDE list
-
Hi, within a script this will be complicated since there is no built-in way for listening to the end of a rendering in the BatchRender.
But with a plugin, you could have a timer (see MessageData Documentation) that constantly check if the BatchRender is rendering with BatchRender.IsRendering and if it is not rendering then you can modify the file, save it and add it to the BatchRender again.
You will also most likely need a CommandData plugin so this way the user can start the execution. This command data can just define a global boolean value that your Timer message always check and if the value is True, then it start the processing of a file.One stupid question, but at this point since you will need to do all the queues logic, why not directly render the document, using RenderDocument then you do not need a timer neither to save the doc each time.
Cheers,
Maxime. -
Yes, I was able to make it work by pausing the script until IsRendering returns false. It is working really nice, naming my jpegs correctly and is very simple to setup by just dropping the objects I want rendered into the In/Ex list. This workflow is great when I have over 100 objects I want rendered on their own.
I could use RenderDocument and the Take system but I have not developed a python way to iterate through scene objects - make object visible - assign a take with visibility settings - render with only that object visible. I do believe it is possible and another route I could take. I may try to develop this method as well to compare workflow but I find the Take system cumbersome for bigger jobs.
Cheers,
Gabriel -
Ok, I ran into the issue of polling the BatchRender.IsRendering. I don't have the python skills to do multi-threading to check when a render is complete. I was able to hack in a time.sleep function but it is very problematic for longer renders and crashes a lot.
Instead I went the route of Takes though Take are a bit cumbersome to setup but it is working nicely. I'll post the code here for anyone looking to do something similiar. I can just RenderAllTakes command and get each object rendered with its name using a $Take render token.
def main(): doc = c4d.documents.GetActiveDocument() inEx = op[c4d.ID_USERDATA,4] # In/Exclude User Data for i in range(0 , op[c4d.ID_USERDATA,4].GetObjectCount()): #Iterate through each shot based on our shot slider in user data activeObj = inEx.ObjectFromIndex(doc, i) #deterimine which object is active if op[c4d.ID_USERDATA,1] == i: activeObj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = False #turn visibility On activeObj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = False render_filename = inEx.ObjectFromIndex(doc, i).GetName() #Gets the name of the currently active object op[c4d.ID_USERDATA,5] = render_filename else: activeObj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = True #turn visibility Off activeObj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = True c4d.EventAdd() ################ SEND ALL def BuildTakes(): #Sequencer() takedata = doc.GetTakeData() obj = doc.GetActiveObject() #print( Description.GetParameterI(op[c4d.ID_USERDATA,1], ar=None)) inEx = op[c4d.ID_USERDATA,4] # In/Exclude User Data for i in range(0,op[c4d.ID_USERDATA,4].GetObjectCount()): #Sequencer() take = takedata.AddTake(inEx.ObjectFromIndex(doc, i).GetName(), None, None) take.FindOrAddOverrideParam(takedata, obj , c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 1),c4d.DescLevel(1)), i , backupValue=None, deleteAnim=False) take.FindOrAddOverrideParam(takedata, inEx.ObjectFromIndex(doc, i), c4d.ID_BASEOBJECT_VISIBILITY_EDITOR, False, backupValue=None, deleteAnim=False) take.FindOrAddOverrideParam(takedata, inEx.ObjectFromIndex(doc, i), c4d.ID_BASEOBJECT_VISIBILITY_RENDER, False, backupValue=None, deleteAnim=False) #op[c4d.ID_USERDATA,1] = 0 #Sequencer() #c4d.MSG_DESCRIPTION_CHECKUPDATE #Sequencer() #c4d.EventAdd() def ClearTakes(): tk = doc.GetTakeData() tk.ResetSystem()
-
This post is deleted!