Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
Hi all,
Apologies for the delay, but only had time to revisit this issue this week. So I found the solution and thought about posting here for other people. It's probably not the best one, but it works for me and I'm happy with the final results.
Like to thank Niklas, Scott and Riccardo for helping with this, as a lot of the information came from them, which I just adapted. So this is developed to render with Octane, but you can change to whatever render settings you need.
""" The script is a blend of techniques that I found to be working at the end. It starts by getting all of the polygon object in the scene, iterating through each object and soloing it in a layer. Creates the objects, camera, materials and render settings for the environment. Focus the object first before calculating if all of the object is in the scene. Because the camera has a target tag connected to a moving null that sits under each object is rendering, the position reaches a conclusion when object is inside the camera frame. I didn't want to create a new thread to run the script as I prefer the user not be using cinema and let it run. We can't afford to have Cinema crashing at the same time. """ import c4d def get_op(op, name): while op: if op.GetName() == name: return op get_op(op.GetDown(), name) op = op.GetNext() def get_mat(op, name): while op: if op.GetName() == name: return op get_op(op.GetDown(), name) op = op.GetNext() def get_op_type(op, name, lst): while op: if op.GetTypeName() == name: lst.append(op) get_op_type(op.GetDown(), name, lst) op = op.GetNext() return lst def get_vp(op, name): while op: if op.GetName() == name: return op op = op.GetNext() def create_obj(op_type, name): op = c4d.BaseObject(op_type) op.SetName(name) doc.InsertObject(op) return op def create_render(doc): rd = c4d.documents.RenderData() doc.InsertRenderData(rd) doc.SetActiveRenderData(rd) rd.SetName('Temp_BP') rd[c4d.RDATA_RENDERENGINE] = 1029525 oct_render_vp = c4d.documents.BaseVideoPost(1029525) rd.InsertVideoPost(oct_render_vp) rd[c4d.RDATA_XRES] = 540 rd[c4d.RDATA_YRES] = 540 rd[c4d.RDATA_LOCKRATIO] = True rd[c4d.RDATA_FILMASPECT] = 1.0 rd[c4d.RDATA_FRAMESEQUENCE] = 1 rd[c4d.RDATA_FRAMERATE] = 24 # Save rd[c4d.RDATA_GLOBALSAVE] = True rd[c4d.RDATA_SAVEIMAGE] = True rd[c4d.RDATA_FORMAT] = c4d.FILTER_PNG rd[c4d.RDATA_NAMEFORMAT] = 6 rd[c4d.RDATA_FORMATDEPTH] = 1 # Octane Renderer oct_render_vp[c4d.SET_PASSES_ENABLED] = False oct_render_vp[c4d.VP_UPDATE_MATERIALS_MODE] = False oct_render_vp[1013] = 0 oct_render_vp[c4d.VP_BUFFER_TYPE] = 2 oct_render_vp[c4d.VP_KERNEL_ENABLE] = True oct_render_vp[c4d.SET_DIRECT_MAXSAMPLES] = 1000 oct_render_vp[c4d.SET_DIRECT_SPECDEPTH] = 3 oct_render_vp[c4d.SET_DIRECT_AODIST] = 0.3 oct_render_vp[c4d.SET_DIRECT_ALPHACHAN] = True oct_render_vp[c4d.SET_DIRECT_KEEPENV] = False oct_render_vp[c4d.SET_DIRECT_ADAPTIVE_SAMPLING] = True oct_render_vp[c4d.SET_DIRECT_ASAMP_EXP_EXPOSURE] = 1.0 # Watermark watermark_vp = c4d.documents.BaseVideoPost(1025462) rd.InsertVideoPostLast(watermark_vp) watermark_vp[c4d.VP_WATERMARK_TEXT_ENABLE] = True watermark_vp[c4d.VP_WATERMARK_TEXT_SHOWLABELS] = False watermark_vp[c4d.VP_WATERMARK_TEXT_RENDERTIME] = False watermark_vp[c4d.VP_WATERMARK_TEXT_DATE] = False watermark_vp[c4d.VP_WATERMARK_TEXT_BG_OPACITY] = 1.0 rd.Message(c4d.MSG_UPDATE) return (rd, watermark_vp) def TestPointInFrame(pt, frame): return pt.x > frame['cl'] and pt.x < frame['cr'] and pt.y > frame['ct'] and pt.y < frame['cb'] def CheckIfInView(cam, obj, doc): # Get the current BaseDraw bd = doc.GetActiveBaseDraw() safeFrame = bd.GetSafeFrame() # Get the active object bouding box center and radius box = [c4d.Vector() for x in xrange(8)] points = [c4d.Vector() for x in xrange(8)] rd = obj.GetRad() mp = obj.GetMp() # Build the active object bouding box box[0] = c4d.Vector() box[0].x = mp.x - rd.x box[0].y = mp.y - rd.y box[0].z = mp.z - rd.z box[0] *= obj.GetMgn() box[1] = c4d.Vector() box[1].x = mp.x - rd.x box[1].y = mp.y + rd.y box[1].z = mp.y - rd.z box[1] *= obj.GetMgn() box[2] = c4d.Vector() box[2].x = mp.x + rd.x box[2].y = mp.y - rd.y box[2].z = mp.y - rd.z box[2] *= obj.GetMgn() box[3] = c4d.Vector() box[3].x = mp.x + rd.x box[3].y = mp.y + rd.y box[3].z = mp.y - rd.z box[3] *= obj.GetMgn() box[4] = c4d.Vector() box[4].x = mp.x + rd.x box[4].y = mp.y - rd.y box[4].z = mp.z + rd.z box[4] *= obj.GetMgn() box[5] = c4d.Vector() box[5].x = mp.x + rd.x box[5].y = mp.y + rd.y box[5].z = mp.y + rd.z box[5] *= obj.GetMgn() box[6] = c4d.Vector() box[6].x = mp.x - rd.x box[6].y = mp.y - rd.y box[6].z = mp.y + rd.z box[6] *= obj.GetMgn() box[7] = c4d.Vector() box[7].x = mp.x - rd.x box[7].y = mp.y + rd.y box[7].z = mp.y + rd.z box[7] *= obj.GetMgn() # Calculate bouding box coordinates in screen space for i in xrange(len(box)): points[i] = bd.WS(box[i]) # Test if the current object is completely visible in the rendered safe frame for i in xrange(len(points)): visible = TestPointInFrame(points[i], safeFrame) if not visible: break return visible def escaped_pressed(bc=None): if bc is None: bc = c4d.BaseContainer() c4d.gui.GetInputEvent(c4d.BFM_INPUT_KEYBOARD, bc) return bc[c4d.BFM_INPUT_CHANNEL] == c4d.KEY_ESC def main(): doc = c4d.documents.GetActiveDocument() first_obj = doc.GetFirstObject() first_mat = doc.GetFirstMaterial() sku_layer = first_obj.GetLayerObject(doc) objs_lst = get_op_type(first_obj, 'Polygon', []) count_total = len(objs_lst) rs, water_vp = create_render(doc) cam = get_op(first_obj, 'Temp_Cam') bg = get_op(first_obj, 'BG') mat = get_mat(first_mat, 'BG_Mat') trg_null = get_mat(first_mat, 'Cam_Target') if cam == None: cam = create_obj(c4d.Ocamera, 'Temp_Cam') oct_cam_tag = cam.MakeTag(1029524) # Octane Camera tag oct_cam_tag[c4d.OCTANECAMERA_ENABLE_IMAGER] = True oct_cam_tag[c4d.OCTANECAMERA_PREMALPHA] = True trg_tag = cam.MakeTag(c4d.Ttargetexpression) if trg_null == None: trg_null = create_obj(c4d.Onull, 'Cam_Target') trg_tag[c4d.TARGETEXPRESSIONTAG_LINK] = trg_null if bg == None: bg = create_obj(c4d.Obackground, 'BG') tex_tag = bg.MakeTag(c4d.Ttexture) if mat == None: mat = c4d.BaseMaterial(1029501) mat.SetName('BG_Mat') shader = c4d.BaseList2D(1011100) shader[c4d.SLA_GRADIENT_TYPE] = 2002 # Type 2D - Diagonal grad = shader[c4d.SLA_GRADIENT_GRADIENT] grad.FlushKnots() k1 = grad.InsertKnot(col=c4d.Vector(0.5, 0.5, 0.5), pos=0.25, index = 0) k2 = grad.InsertKnot(col=c4d.Vector(0.25, 0.25, 0.25), pos=0.75, index= 1) shader[c4d.SLA_GRADIENT_GRADIENT] = grad mat[c4d.OCT_MATERIAL_DIFFUSE_LINK] = shader mat.InsertShader(shader) mat.Message(c4d.MSG_UPDATE) # Update the material changes mat.Update(True, True) # Update the material's icon image doc.InsertMaterial(mat) tex_tag.SetMaterial(mat) bd = doc.GetActiveBaseDraw() bd.SetSceneCamera(cam) root_layer = doc.GetLayerObjectRoot() child_layer = root_layer.GetDown() layer = None while child_layer: if child_layer.GetName() == 'Temp_Layer': layer = child_layer break child_layer = child_layer.GetNext() if not layer: layer = c4d.documents.LayerObject() layer.SetName('Temp_Layer') layer.InsertUnder(root_layer) cam.SetLayerObject(layer) bg.SetLayerObject(layer) hdri = get_op(first_obj, 'HDRI') hdri.SetLayerObject(layer) subd_lst = get_op_type(first_obj, 'Subdivision Surface', []) for subd in subd_lst: subd.SetLayerObject(layer) if not objs_lst: c4d.gui.MessageDialog('No polygon objects in the scene!') c4d.StatusSetText('No polygon objects in the scene!') return False for count, obj in enumerate(objs_lst[:10], start=1): rad_x = obj.GetRad().x * 2 rad_y = obj.GetRad().y * 2 rad_z = obj.GetRad().z * 2 water_vp[c4d.VP_WATERMARK_TEXT_CUSTOM_TEXT] = 'Dimensions(cm): (H){0:.2f} x (W){1:.2f} x (D){2:.2f}'.format(rad_x, rad_y, rad_z) trg_null.Remove() trg_null.InsertUnder(obj) doc.SetActiveObject(obj) obj.SetLayerObject(layer) obj.SetRenderMode(c4d.MODE_ON) layer_data = layer.GetLayerData(doc) layer_data['solo'] = True layer.SetLayerData(doc,layer_data) doc.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_SET) c4d.CallCommand(12151) doc.SetActiveObject(None) cam.Message(c4d.MSG_UPDATE) scaleFactor = c4d.Vector(15, 15, cam.GetMg().off.z) # <---- Change the vector values for camera position. scaleFactor = scaleFactor.__mul__(0.05) stopped = False c4d.StatusSetSpin() while not CheckIfInView(cam, obj, doc): camMg = cam.GetMg() camMgOff = camMg.off camMgOff += scaleFactor camMg.off = camMgOff cam.SetMg(camMg) c4d.DrawViews(c4d.DRAWFLAGS_NO_THREAD | c4d.DRAWFLAGS_FORCEFULLREDRAW) stopped = escaped_pressed() if stopped: break render_set = doc.GetFirstRenderData() parent_name = obj.GetUp().GetName() obj_name = '{0}_{1}_{2}'.format(doc.GetDocumentName().split('.')[0], parent_name, obj.GetName()) render_set[c4d.RDATA_PATH] = '../Renders/{0}'.format(obj_name) # <----- Make sure to correct File Path rs = render_set.GetData() x_res = int(rs[c4d.RDATA_XRES]) y_res = int(rs[c4d.RDATA_YRES]) bmp = c4d.bitmaps.BaseBitmap() bmp.Init(x=x_res, y=y_res, depth=24) c4d.documents.RenderDocument(doc, rs, bmp, c4d.RENDERFLAGS_EXTERNAL) bd.Message(c4d.MSG_CHANGE) obj.SetRenderMode(c4d.MODE_UNDEF) obj.SetLayerObject(sku_layer) cam.SetAbsPos(c4d.Vector(0)) c4d.StatusClear() print('{0} / {1} - {2} - Render Done!'.format(count, count_total, obj.GetName())) c4d.StatusSetText('{0} / {1} - {2} - Render Done!'.format(count, count_total, obj.GetName())) layer.Remove() doc.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_CLEAR) cam.Remove() trg_null.Remove() bg.Remove() mat.Remove() doc.GetFirstRenderData().Remove() for subd in subd_lst: subd.SetLayerObject(sku_layer) c4d.EventAdd() c4d.gui.MessageDialog('Renders Done!') c4d.StatusSetText('Renders Done!') main()
Hope this helps and please let me know if we can make this better . (Which I'm sure we can!)
Thank you!
Andre
Hi @Cairyn,
Understood and works like a charm!
Many thanks guys
Hi @Cairyn and @ferdinand
Many thanks for your input.
It definitely works if you enable the Camera Dependent flag as Cairyn mentioned. I'm just trying to figure out how to enable it.
tag[c4d.EXPRESSION_PRIORITY].SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True) perhaps? This is not working, so not sure if I should do it via SetParameter.
tag[c4d.EXPRESSION_PRIORITY].SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True)
@ferdinand Cheers for your screenshare and code. Unfortunately, it still happens with your code. If you look through the camera and pan around does the target stays fixed for you?
P.S. What do you use for your screen share gifs, please?
Thank you both!
@fwilleke80 Hi Frank,
Yes, apologies for the low-quality video. Think Google Drive may be compressing it somehow.
Not sure if you noticed, but on the Camera named Script_Target_Tag, I can pan in the viewport and the aim moves with it. Once I click anywhere it jumps back to the targeted Null/Cube. While when added the target tag manually, it's always aimed at the targeted Null/Cube.
Hope this makes sense.
Let me know if not and will do another comparison video.
Thank you in advance,
We recently moved from Cinema R19 to R21 and manage to do the changes to our libraries. One of the things I noticed is that when creating a target tag for a camera cam.MakeTag(c4d.Ttargetexpression) does not work correctly when assigning a target, as the camera just moves and jumps around the viewport. Added screen share below of a camera with added scripted target tag and a camera with added manual target tag.
cam.MakeTag(c4d.Ttargetexpression)
Screenshare
Am I missing something really obvious with the change between Cinema versions, please?
Thank you all in advance,
@ferdinand Hi Ferdinand,
Thank you for your time and help! Will get it sorted with your pointers above.
All the best!
@ferdinand I just found what perhaps is the issue...
The world-container. It seems that it runs without freezing if the data is not saved and load from the world container. Quiet odd, but perhaps as you said, it corrupted it somehow?
Just need to find a way to storing data outside of the document.
Many thanks for your valid points and I appreciate what you are saying :).
I've made the changes to use a GeDialog instead for the menu and it works.
GeDialog
Here's are my results, based on your proposals:
ShowPopupDialog()
GetTimer()
The undo-block is for something I wrote in between, but for simplification removed it to pinpoint the culprit.
The world-container is definitely a good point. I'm not sure if I should be using it to store data. I think the reasoning behind it was to keep data accessible between loading and unloading of documents, which is mainly what this plugin is doing. Would you recommend perhaps a different container to store the data?
What I found most weird is that works perfectly fine with R19, so perhaps as you pointed out on 2, Cinema needs reinstalling.
It's all learning and I think I know a bit more about it now and even if I cannot find the solution for this, I already have another way of dealing with it. Not elegant, but works with no issues :).
Thank you very much for your help and time!
Thank you for your tips!
Unfortunately removing the other plugins does not make a difference, from what I can understand.
I've also run the general popup dialog and it works fine with no freezing.
Not sure what to do about this except perhaps change the c4d.gui.ShowPopupDialog to a c4d.gui.GeDialog and create a simple menu. Any thoughts?
c4d.gui.ShowPopupDialog
c4d.gui.GeDialog
Thank you in advance!
@m_adam Morning Maxime,
Yes, it freezes with the code I provided. I've also tried the way that you mention above by running the c4d.SpecialEventAdd(1055684) and the same happens, as per my video below.
c4d.SpecialEventAdd(1055684)
https://drive.google.com/file/d/1v4A9xXKb79Z1TlMnlSdZ-tk9WJFldsv7/view?usp=sharing
Let me know if you need any other information.
@m_adam Hi Maxime,
Cinema R21.207 Build RB303831. Windows 10 1809
I'm calling the SpecialEventAdd on an SceneLoaderPlugin.
SpecialEventAdd
# Import modules import c4d plugin_ID = 1055684 class AOLoader(c4d.plugins.SceneLoaderData): """ Class for the SceneLoaderData plugin. """ def Identify(self, node, name, probe, size): """ Identifies if the format of the custom file captured meets the plugins requirements. :param node: BaseContainer for instance. :param name: Path and file name of the instance. :param probe: The data from the instance. :param size: The data size of the instance. :return: True if meets object requirements. """ file_name = name.lower() if not file_name.endswith('.ao'): return False return True @staticmethod def clean_path_str(txt, chr_lst): """ This method removes specific characters in a path. :param txt: The string to replace character. :param chr_lst: The characters list to be replaced. :return: Returns the path with removed c characters. """ for c in chr_lst: txt = txt.replace(c, '') return txt def read_lines(self, data_file): """ Iterates throught the data until finds the line with path for .c4d asset. :param data_file: The file data returned by Identify method. :return: The path string, not formatted. """ with open(data_file, 'rU') as data: ds_file = data.read().splitlines() paths = "" for line in ds_file: if ".c4d" in line: remove_chrs = self.clean_path_str(line, [')', '"']) paths += remove_chrs.replace("\\\\", "\\") return paths def Load(self, node, name, doc, filterflags, error, bt): """ Loads the data into the active document. In this case, we do not load or merge the file, but send a message to CoreMessage when it is catched with a SpecialEventAdd. The WorldContainer will save the path to be used later. :param node: BaseContainer for the instance. :param name: Path and file name of the instance. :param doc: The active document. :param filterflags: Flags to filter objects to load. :param error: Errors brought by Identify Method. :param bt: The BaseThread for the document. :return: Returns a c4d.FILEERROR_USERBREAK to capture the message. """ paths = self.read_lines(name) world_container = c4d.GetWorldContainerInstance() world_container.SetString(plugin_ID, paths) c4d.SpecialEventAdd(plugin_ID) return c4d.FILEERROR_USERBREAK if __name__ == "__main__": c4d.plugins.RegisterSceneLoaderPlugin(id=plugin_ID, str="AO Loader", g=AOLoader, info=c4d.PLUGINFLAG_SCENELOADER_MERGEORIGINAL, description="")
It's passing the data correctly, so perhaps the call needs to be done somewhere else?