Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Render depth map using python

    Cinema 4D SDK
    python
    2
    3
    469
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • G
      Gregor M
      last edited by

      I'm creating a simple Python plugin and i need to render depth map using c4d.RENDERFLAGS_EXTERNAL. I'm not sure how to do this.

      import c4d
      from c4d import gui, plugins, storage
      import os
      
      PLUGIN_ID = 1234567  
      
      def GetFilterTypeFromFilename(filename):
          ext = os.path.splitext(filename)[1].lower()
          if ext == '.bmp':
              return c4d.FILTER_BMP
          elif ext == '.jpg' or ext == '.jpeg':
              return c4d.FILTER_JPG
          elif ext == '.png':
              return c4d.FILTER_PNG
          elif ext == '.tif' or ext == '.tiff':
              return c4d.FILTER_TIF
          elif ext == '.exr':
              return c4d.FILTER_EXR
          else:
              return c4d.FILTER_PNG  # Default to PNG if extension is unrecognized
      
      class RenderDialog(gui.GeDialog):
          DESTINATION_FILE = 1001
          BROWSE_BUTTON = 1002
          RENDER_BUTTON = 2000
      
          def CreateLayout(self):
              self.SetTitle("External Render Plugin")
      
              # Add Destination File Field
              self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, cols=2)
              self.AddStaticText(id=0, flags=c4d.BFH_LEFT, name="Destination File:")
              self.AddEditText(id=self.DESTINATION_FILE, flags=c4d.BFH_SCALEFIT)
              self.GroupEnd()
      
              # Add Browse Button
              self.AddButton(id=self.BROWSE_BUTTON, flags=c4d.BFH_LEFT, name="Browse")
      
              # Add Render Button
              self.AddButton(id=self.RENDER_BUTTON, flags=c4d.BFH_CENTER, name="Render")
      
              return True
      
          def Command(self, id, msg):
              if id == self.BROWSE_BUTTON:
                  path = storage.SaveDialog(title="Select Destination File")
                  if path:
                      self.SetString(self.DESTINATION_FILE, path)
                  return True
      
              elif id == self.RENDER_BUTTON:
                  path = self.GetString(self.DESTINATION_FILE)
                  if not path:
                      gui.MessageDialog("Please specify a destination file.")
                      return True
      
                  # Get the active document and render settings
                  doc = c4d.documents.GetActiveDocument()
                  rd = doc.GetActiveRenderData()
                  rdata = rd.GetDataInstance().GetClone(c4d.COPYFLAGS_NONE)
      
                  # Set render settings
                  rdata[c4d.RDATA_PATH] = path
                  rdata[c4d.RDATA_RENDERENGINE] = c4d.RDATA_RENDERENGINE_STANDARD
      
                  # Create a bitmap to render into
                  width = int(rdata[c4d.RDATA_XRES])
                  height = int(rdata[c4d.RDATA_YRES])
                  bmp = c4d.bitmaps.BaseBitmap()
                  result = bmp.Init(width, height)
                  if result != c4d.IMAGERESULT_OK:
                      gui.MessageDialog("Failed to initialize bitmap.")
                      return True
      
                  # Trigger render
                  render_result = c4d.documents.RenderDocument(
                      doc,       # Document to render
                      rdata,     # Render settings
                      bmp,       # Bitmap to render into
                      c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_DONTANIMATE  # Flags
                  )
      
                  if render_result != c4d.RENDERRESULT_OK:
                      gui.MessageDialog("Render failed.")
                  else:
                      # Determine the appropriate file format filter
                      filter_type = GetFilterTypeFromFilename(path)
                      # Save the rendered image
                      save_result = bmp.Save(path, filter_type, c4d.BaseContainer())
                      if save_result != c4d.IMAGERESULT_OK:
                          gui.MessageDialog("Failed to save image.")
                      else:
                          gui.MessageDialog("Render completed and saved.")
      
                  return True
      
              return False
      
      class ExternalRenderPlugin(plugins.CommandData):
          def __init__(self):
              self.dlg = None
      
          def Execute(self, doc):
              if not self.dlg:
                  self.dlg = RenderDialog()
              self.dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=100)
              return True
      
      if __name__ == "__main__":
          plugins.RegisterCommandPlugin(
              id=PLUGIN_ID,
              str="External Render Plugin",
              info=0,
              icon=None,
              help="Render current frame to a specified file",
              dat=ExternalRenderPlugin()
          )
      
      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @Gregor M
        last edited by ferdinand

        Hello @Gregor-M,

        Thank you for reaching out to us. You cannot really use c4d.documents.RenderDocument for this as it does not rally support multipass output. But you can use the render queue.

        Cheers,
        Ferdinand

        Result

        3207b4cc-fc47-4800-9359-f7fb753fab58-image.png

        Code

        """Renders a depth channel for the active document and shows it in the picture viewer.
        
        This script is a demonstration of how to render a depth pass for the active document and show it in
        the picture viewer. It does so by saving the document to a temporary file, setting up the depth
        pass, and then rendering the document using the batch renderer. After the rendering is done, the
        depth pass is loaded and shown in the picture viewer.
        
        We cannot use RenderDocument() to render a depth pass, as it does not support multipass channels. 
        And we also cannot just use the render command, as a script manager script is blocking for commands,
        i.e., the render command will only be executed after the script has finished.
        
        # Requirements
        
        - The document must be saved before rendering.
        - The render engine must be set to Standard Renderer, as the depth pass is not supported by all
          other engines. If yours does, you can remove or adjust the check in the script. But Redshift has
          for example its own pass system.
        """
        
        import c4d
        import os
        import time
        import mxutils
        
        doc: c4d.documents.BaseDocument # The active document.
        
        def EnableMultiPass(rData: c4d.documents.RenderData, passType: int) -> None:
            """Assures that the given #rData has a multipass of the given #passType.
            """
            # Find an existing pass of the given type.
            hasPass: bool = False
            element: c4d.BaseList2D = rData.GetFirstMultipass()
            while element:
                if element.GetDataInstance()[c4d.MULTIPASSOBJECT_TYPE] == passType:
                    element.DelBit(c4d.BIT_VPDISABLED) # Make sure it is enabled.
                    hasPass = True
                    break
                element = element.GetNext()
        
            # If the pass was not found, create a new one.
            if not hasPass:
                element: c4d.BaseList2D = mxutils.CheckType(c4d.BaseList2D(c4d.Zmultipass))
                element[c4d.MULTIPASSOBJECT_TYPE] = passType
                rData.InsertMultipass(element)
        
        def main():
            """Run the script.
            """
            # Get the name and path of the current document and bail when it is an unsaved document.
            name: str = os.path.splitext(doc.GetDocumentName())[0]
            path: str = doc.GetDocumentPath()
            if not path:
                raise ValueError("The document must be saved before rendering.")
        
            # Get the batch render and bail when it is already running.
            batch: c4d.documents.BatchRender = c4d.documents.GetBatchRender()
            if batch.IsRendering():
                raise ValueError("The batch render is already running.")
        
            # Define a path for a copy of this document and a path to save the multipass images to.
            documentPath: str = os.path.join(path, f"{name}_job.c4d")
            multipassPath: str = os.path.join(path, f"{name}_job_multipass")
            depthPngPath: str = f"{multipassPath}_depth.png"
        
            # Get the render data from the document and get and set the relevant settings.
            rData: c4d.documents.RenderData = doc.GetActiveRenderData()
            if rData[c4d.RDATA_RENDERENGINE] != c4d.RDATA_RENDERENGINE_STANDARD:
                raise ValueError("The render engine must be set to Standard Renderer.")
        
            # Setup the depth pass.
            EnableMultiPass(rData, c4d.VPBUFFER_DEPTH)
            rData[c4d.RDATA_MULTIPASS_ENABLE] = True
            rData[c4d.RDATA_MULTIPASS_SAVEIMAGE] = True
            rData[c4d.RDATA_MULTIPASS_SAVEONEFILE] = False
            rData[c4d.RDATA_MULTIPASS_SAVEFORMAT] = c4d.FILTER_PNG
            rData[c4d.RDATA_MULTIPASS_FILENAME] = multipassPath
        
            # We cannot use RenderDocument() to render a depth pass, as it does not support multipass
            # channels. So, we must use the render queue and for that also setup the render data (which
            # is also why we did set the multipass save settings above).
        
            # Save the new file state to #documentPath and then push it into the render queue.
            if not c4d.documents.SaveDocument(doc, documentPath, c4d.SAVEDOCUMENTFLAGS_0, c4d.FORMAT_C4DEXPORT):
                raise ValueError("Failed to save the document.")
        
            # Disable all possibly already existing jobs in the batch renderer, add a new job in front,
            # and carry out the rendering.
            for i in range(batch.GetElementCount()):
                batch.EnableElement(i, False)
        
            batch.AddFile(documentPath, 0)
            batch.SetRendering(c4d.BR_START)
            c4d.StatusSetText(f"Rendering {name}...")
            c4d.StatusSetSpin()
            while batch.IsRendering():
                time.sleep(.25)
        
            # The batch render is done, we check if the depth pass exists and load it.
            c4d.StatusClear()
            if not os.path.exists(depthPngPath):
                raise ValueError(f"Failed to render the multipass image to {depthPngPath}.")
        
            bmp: c4d.bitmaps.BaseBitmap = c4d.bitmaps.BaseBitmap()
            if not bmp.InitWith(depthPngPath):
                raise ValueError(f"Failed to load bitmap from: {depthPngPath}")
        
            # Do something with the bitmap, we just show it in the picture viewer.
            c4d.bitmaps.ShowBitmap(bmp)
        
        
        if __name__=='__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • G
          Gregor M
          last edited by

          Thank you for the quick answer.
          Unfortunately this approach was quite destructive for my workflow, so i found a workaround using materials and 3d gradients. For anyone who want to render z-depth in python from c4d scene, this may be the approach. There may be some tweaks considering color space.
          It would be nice to get access to this kind of data inside cinema 4d's python without crazy workarounds.

          import c4d
          from c4d import gui, plugins, storage
          import os
          
          PLUGIN_ID = 1234567  # Replace this ID with your unique plugin ID from Maxon
          
          def GetFilterTypeFromFilename(filename):
              ext = os.path.splitext(filename)[1].lower()
              if ext == '.bmp':
                  return c4d.FILTER_BMP
              elif ext == '.jpg' or ext == '.jpeg':
                  return c4d.FILTER_JPG
              elif ext == '.png':
                  return c4d.FILTER_PNG
              elif ext == '.tif' or ext == '.tiff':
                  return c4d.FILTER_TIF
              elif ext == '.exr':
                  return c4d.FILTER_EXR
              else:
                  return c4d.FILTER_PNG  # Default to PNG if extension is unrecognized
          
          class RenderDialog(gui.GeDialog):
              DESTINATION_FILE = 1001
              BROWSE_BUTTON = 1002
              RENDER_BUTTON = 2000
          
              def CreateLayout(self):
                  self.SetTitle("External Render Plugin")
          
                  # Add Destination File Field
                  self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, cols=2)
                  self.AddStaticText(id=0, flags=c4d.BFH_LEFT, name="Destination File:")
                  self.AddEditText(id=self.DESTINATION_FILE, flags=c4d.BFH_SCALEFIT)
                  self.GroupEnd()
          
                  # Add Browse Button
                  self.AddButton(id=self.BROWSE_BUTTON, flags=c4d.BFH_LEFT, name="Browse")
          
                  # Add Render Button
                  self.AddButton(id=self.RENDER_BUTTON, flags=c4d.BFH_CENTER, name="Render")
          
                  return True
          
              def Command(self, id, msg):
                  if id == self.BROWSE_BUTTON:
                      path = storage.SaveDialog(title="Select Destination File")
                      if path:
                          self.SetString(self.DESTINATION_FILE, path)
                      return True
          
                  elif id == self.RENDER_BUTTON:
                      path = self.GetString(self.DESTINATION_FILE)
                      if not path:
                          gui.MessageDialog("Please specify a destination file.")
                          return True
          
                      # Get the active document
                      doc = c4d.documents.GetActiveDocument()
          
                      # Create a large sphere object
                      sphere = c4d.BaseObject(c4d.Osphere)
                      sphere[c4d.PRIM_SPHERE_RAD] = 10000000  # Set radius to 10,000,000 m
                      doc.InsertObject(sphere)
                      c4d.EventAdd()
          
                      # Check if a material named "LuminanceMaterial" already exists
                      material = doc.SearchMaterial("LuminanceMaterial")
                      if not material:
                          # Create new material and set its parameters
                          material = c4d.BaseMaterial(c4d.Mmaterial)
                          material.SetName("LuminanceMaterial")
                          material[c4d.MATERIAL_USE_COLOR] = False
                          material[c4d.MATERIAL_USE_REFLECTION] = False
                          material[c4d.MATERIAL_USE_LUMINANCE] = True
          
                          # Create gradient shader and set it as luminance
                          gradient_shader = c4d.BaseShader(c4d.Xgradient)
                          if gradient_shader:
                              gradient_shader[c4d.SLA_GRADIENT_TYPE] = c4d.SLA_GRADIENT_TYPE_3D_LINEAR  # 3D Linear Gradient
                              gradient_shader[c4d.SLA_GRADIENT_SPACE] = c4d.SLA_GRADIENT_SPACE_WORLD  # World space
                              gradient_shader[c4d.SLA_GRADIENT_CYCLE] = False
          
                              material[c4d.MATERIAL_LUMINANCE_SHADER] = gradient_shader
                              material.InsertShader(gradient_shader)
          
                          # Insert material into the document
                          doc.InsertMaterial(material)
          
                      # Update gradient start and end points for the current camera
                      gradient_shader = material[c4d.MATERIAL_LUMINANCE_SHADER]
                      if gradient_shader:
                          camera = doc.GetActiveObject()
                          if not camera or camera.GetType() != c4d.Ocamera:
                              camera = doc.GetRenderBaseDraw().GetSceneCamera(doc)
                          if not camera:
                              camera = doc.GetRenderBaseDraw().GetEditorCamera()
          
                          if camera:
                              start_position = camera.GetMg().off
                              focus_distance = camera[c4d.CAMERAOBJECT_TARGETDISTANCE]
                              end_position = start_position + (camera.GetMg().v3 * focus_distance)  # Use v3 for z-axis direction
                              gradient_shader[c4d.SLA_GRADIENT_START] = start_position
                              gradient_shader[c4d.SLA_GRADIENT_END] = end_position
          
                      # Enable material override in render settings
                      rd = doc.GetActiveRenderData()
                      rd[c4d.RDATA_MATERIAL_OVERRIDE] = True
                      rd[c4d.RDATA_MATERIAL_OVERRIDE_LINK] = material
          
                      # Set render settings
                      rdata = rd.GetDataInstance().GetClone(c4d.COPYFLAGS_NONE)
                      rdata[c4d.RDATA_PATH] = path
                      rdata[c4d.RDATA_RENDERENGINE] = c4d.RDATA_RENDERENGINE_STANDARD
          
                      # Create a bitmap to render into
                      width = int(rdata[c4d.RDATA_XRES])
                      height = int(rdata[c4d.RDATA_YRES])
                      bmp = c4d.bitmaps.BaseBitmap()
                      result = bmp.Init(width, height)
                      if result != c4d.IMAGERESULT_OK:
                          gui.MessageDialog("Failed to initialize bitmap.")
                          return True
          
                      # Trigger render
                      render_result = c4d.documents.RenderDocument(
                          doc,       # Document to render
                          rdata,     # Render settings
                          bmp,       # Bitmap to render into
                          c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_DONTANIMATE  # Flags
                      )
          
                      if render_result != c4d.RENDERRESULT_OK:
                          gui.MessageDialog("Render failed.")
                      else:
                          # Determine the appropriate file format filter
                          filter_type = GetFilterTypeFromFilename(path)
                          # Save the rendered image
                          save_result = bmp.Save(path, filter_type, c4d.BaseContainer())
                          if save_result != c4d.IMAGERESULT_OK:
                              gui.MessageDialog("Failed to save image.")
                          else:
                              gui.MessageDialog("Render completed and saved.")
          
                      # Turn off material override after rendering
                      rd[c4d.RDATA_MATERIAL_OVERRIDE] = False
                      rd[c4d.RDATA_MATERIAL_OVERRIDE_LINK] = None
          
                      # Delete the large sphere after rendering
                      sphere.Remove()
                      c4d.EventAdd()
          
                      # Delete the material after rendering
                      if material:
                          material.Remove()
                          c4d.EventAdd()
          
                      # Refresh the document
                      c4d.EventAdd()
          
                      return True
          
                  return False
          
          class ExternalRenderPlugin(plugins.CommandData):
              def __init__(self):
                  self.dlg = None
          
              def Execute(self, doc):
                  if not self.dlg:
                      self.dlg = RenderDialog()
                  self.dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=100)
                  return True
          
          if __name__ == "__main__":
              plugins.RegisterCommandPlugin(
                  id=PLUGIN_ID,
                  str="External Render Plugin",
                  info=0,
                  icon=None,
                  help="Render current frame to a specified file",
                  dat=ExternalRenderPlugin()
              )
          
          1 Reply Last reply Reply Quote 0
          • First post
            Last post