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
@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?
Hope you are well in these crazy times!
I'm having trouble understanding how ShowPopupDialog currently works in R21, if caught by MessageData plugin's CoreMessage. It keeps freezing Cinema, could be thread related (which I don't know a lot of)?
ShowPopupDialog
CoreMessage
I manage to get the data across externally fine but when creating the menu it freezes immediately after c4d.gui.ShowPopupDialog is called.
# Import modules import os import c4d from c4d import documents plugin_ID = 1055685 plugin_ID_loader = 1055684 MENU_ADD_ASSETS = c4d.FIRST_POPUP_ID MENU_ADD_MATERIALS = c4d.FIRST_POPUP_ID + 1 MENU_REPLACE_ASSETS = c4d.FIRST_POPUP_ID + 2 MENU_REPLACE_MATERIALS = c4d.FIRST_POPUP_ID + 3 MENU_REPLACE_TAG = c4d.FIRST_POPUP_ID + 4 class AOMessage(c4d.plugins.MessageData): """ Class for the MessageData plugin. """ def GetTimer(self): """ Called to return a time in milliseconds to receive timer messages (MSG_TIMER) with that interval in CoreMessage. This method is queried again after each message.. :return: The time in miliseconds. """ return 0 def CoreMessage(self, id, bc): """ Called to receive core messages. :param id: The id of the messages received. :param bc: The BaseContainer objects with message data. :return: True if Message is received. """ if id == plugin_ID_loader: world_container = c4d.GetWorldContainerInstance() doc = documents.GetActiveDocument() paths_str = world_container.GetString(plugin_ID_loader) paths_lst = paths_str.split(', ') active_objs = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0) active_mat = doc.GetActiveMaterial() doc.StartUndo() # Dialog for importing assets menu = c4d.BaseContainer() if active_objs or active_mat: menu.InsData(MENU_REPLACE_ASSETS, 'Replace Asset(s)') menu.InsData(MENU_REPLACE_MATERIALS, 'Replace Material(s)') menu.InsData(MENU_REPLACE_TAG, 'Replace Texture Tag(s)') else: menu.InsData(MENU_ADD_ASSETS, 'Add Asset(s)') menu.InsData(MENU_ADD_MATERIALS, 'Add Material(s)') result = c4d.gui.ShowPopupDialog(cd=None, bc=menu, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS) doc.EndUndo() world_container.RemoveData(plugin_ID_loader) print("Finished") return True if __name__ == "__main__": c4d.plugins.RegisterMessagePlugin(id=plugin_ID, str="AO Message", info=0, dat=AOMessage())
It works perfectly in R19, so I'm not sure what changed since then. Apologies if this as been asked before.
Thank you very much for your help in advance!
Hi @r_gigante,
Not to worry! I understand!
Thanks for your help!
Hi @zipit,
I am somehow even more confused than before, lol. You want the geometry in the screenshot to be subdivided, right? And the geometry is somehow procedurally generated by a Xpresso rig?
I'm sorry if it made it more confusing! The Xpresso just connects the userdata to the generator object. So the subdivisions are controlled by changing the object's number of segments.
I do not quite understand why you cannot just implement that choice of subdivision in that rig? Doing it after the fact seems incredible cumbersome because this is probably not a static polygon mesh, but some kind of generator object that your rig produces there.
That's correct! Which leads me to think that having a poly object in the first place is probably the best way.
I am also just seeing now that you are asking also for a unsubdiving feature in your first post, which is quite a can of worms for an arbitrary mesh. I would go for your LOD-thingy if you see there a solution, because it is probably the fastest solution for you.
Yes, that's what I currently testing. I agree as that being the best way to switch from different resolutions. Hopefully, this will work.
Will keep you posted!
Thanks again!
Hi @Cairyn,
Does your setup react to the global Level of Detail controls?
I'm afraid not! It's all done by changing the number of segments for the objects.
If not, could you use a local LOD object with differently subdivided children?
I'm currently exploring this. I think I will be able to do that. Need to make sure it it does not break with the rest of setup :) Will keep you posted!
Cheers!