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

    List of object visible by a camera within Safe Frames

    Cinema 4D SDK
    s26 python 2023 2024
    3
    8
    1.5k
    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.
    • F
      Futurium
      last edited by Futurium

      Hi,

      I'm looking for a Python solution that can return lists of all objects visible by a camera, including both standard and Redshift Spherical cameras, and within Safe Frames. I want to include all objects, even those partially visible. I've tried using ViewportSelect (code and test scene attached) and GeCollider, but I'm not getting the results I expected, and the code runs slowly. Could you provide some suggestions and a code snippet as a starting point?
      Also - for testing purposes - for the attached scene and cameras, I expect a list of specific objects (below), not all objects currently returned by my script.
      Expected results:
      EnSuite_Flooring_24_Reset
      Bed2_Flooring_14_Reset
      Landing_Flooring_30_Reset
      Bed4_Flooring_18_Reset
      Bed4_Flooring_18_Reset
      Upstairs1_Flooring_26_Reset
      Collider

      Best regards,
      Tomasz

      import c4d
      doc = c4d.documents.GetActiveDocument()
      bd = doc.GetActiveBaseDraw()
      frame = bd.GetFrame()
      
      def get_flooring_objects():
          flooring_objects = []
          doc = c4d.documents.GetActiveDocument()
          helper_node = doc.SearchObject("Helpers")
          helper_categories = helper_node.GetChildren()
          for helper_cat in helper_categories:
              if helper_cat.GetName() == "_Flooring":
                  flooring_objects = helper_cat.GetChildren()
      
          return flooring_objects
      
      left = frame["cl"]
      right = frame["cr"]
      top = frame["ct"]
      bottom = frame["cb"]
      # Calculates the width and height of the screen
      width = right - left + 1
      height = bottom - top + 1
      
      print ("width : {}, height : {}".format(width, height))
      
      # Creates a new ViewportSelect
      vpSelect = c4d.utils.ViewportSelect()
      # Initialise the ViewportSelect with a list of object
      
      ops = get_flooring_objects()
      vpSelect.Init(width, height, bd, ops, c4d.Mpolyedgepoint, True, c4d.VIEWPORTSELECTFLAGS_IGNORE_HIDDEN_SEL)
      
      our_list = []
      
      c4d.CallCommand(13957)  # Clear Console
      
      for w in range(width):
          for h in range(height):
              elements = vpSelect.PickObject(bd, doc, w,h, 1, c4d.VIEWPORT_PICK_FLAGS_OGL_IGNORE_Z)
              our_list.extend(elements)
      
      name_list = []
      for el in our_list:
          name_list.append(el.GetName())
      
      unique_list = sorted(set(name_list))
      for name in unique_list:
          print ("Unique name: {}".format(name))
      

      Scene for testing : https://we.tl/t-FLqG7LPcNu

      i_mazlovI 1 Reply Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov @Futurium
        last edited by i_mazlov

        Hi Tomasz,

        You can get the safe frame using function GetSafeFrame(), which works the same way as function GetFrame() that you have already used in your code.

        The logic for this would be the same as you have in your script: you iterate over pixels (or regions by using rad argument if you don't need a per-pixel precision) and use function PickObject() with a correct set of flags that
        depend on your criteria (what to consider as the object visible by a camera: is it only being in the camera frame or for example also not being covered by other objects).

        Your code looks fine, except that from how I understand your needs you also need flag VIEWPORT_PICK_FLAGS_OGL_ONLY_VISIBLE, which is not available in python.

        Python is also the reason the code is running slow. Since this is quite an expensive task, consider switching to C++.

        The same question was already discussed here: Get visible objects from viewport in commandline

        Please find below a code snippet that shows the described above workflow (although logically it's the same as yours).

        Cheers,
        Ilia

        Code snippet

        import c4d
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        def main() -> None:
            doc = c4d.documents.GetActiveDocument()
            bd = doc.GetRenderBaseDraw()
            safeframe = bd.GetSafeFrame()
            print(safeframe)
        
            step_x, step_y = 4, 4
            objects = set()
            for x in range(safeframe['cl'], safeframe['cr'], step_y):
                for y in range(safeframe['ct'], safeframe['cb'], step_x):
                    foundObjects = c4d.utils.ViewportSelect.PickObject(bd, doc, x, y, rad=2, flags=c4d.VIEWPORT_PICK_FLAGS_OGL_ONLY_TOPMOST)
                    # print(f"({x},{y}): {len(foundObjects)}")
                    objects = objects.union(set(foundObjects))
            for o in objects:
                print(o.GetName())
            return
        
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • F
          Futurium
          last edited by

          Hi Ilia,

          Thank you for guiding me in the right direction. I've revised my code following your advice, and it's working well with standard cameras. However, it's having difficulty with 360 cameras. The code successfully identifies larger objects but struggles to detect smaller ones, even after I increased the accuracy (step=1, radius=1). Do you have any suggestions?

          Best regards,
          Tomasz

          1 Reply Last reply Reply Quote 0
          • F
            Futurium
            last edited by

            Hi @i_mazlov ,

            I'm writing to follow up on my previous message about the issue with 360 cameras in my code as I haven't heard back. Could you please provide any updates or further guidance?

            Best regards,
            Tomasz

            i_mazlovI 1 Reply Last reply Reply Quote 0
            • M
              mogh
              last edited by

              I spent one year going your route and had a very computational expensive ray caster 😉 - but 5 years ago there was no c4d.utils.ViewportSelect.PickObject as far as i know.

              anyway I think the accuracy is depended on your viewport - try a separate view in "Fullscreen" to check ....

              Hint: I found a solution to render the image and select from that.

              1 Reply Last reply Reply Quote 0
              • F
                Futurium
                last edited by

                Hi @mogh,
                Thank you for your suggestion. Currently, I've developed a method where I replace a spherical camera view with a standard camera, capture what's visible, and rotate until I've covered the entire area. This approach meets my goals, although I'm still assessing its efficiency. Your idea of rendering the image and selecting from it sounds like another option and might be a good alternative.

                1 Reply Last reply Reply Quote 0
                • i_mazlovI
                  i_mazlov @Futurium
                  last edited by

                  Hi Tomasz,

                  please excuse the delayed answer.

                  Redshift Spherical camera (as well as e.g. FishEye or Cylindrical) is a special type of the object that is not easy to handle. There's no "working out of the box" solution in your case, so replacing with the ordinary camera and iterating over the sphere sounds like a viable solution to me.

                  Cheers,
                  Ilia

                  MAXON SDK Specialist
                  developers.maxon.net

                  F 1 Reply Last reply Reply Quote 0
                  • F
                    Futurium @i_mazlov
                    last edited by

                    Hi @i_mazlov ,
                    Thank you for confirming my solution.
                    Best regards,
                    Tomasz

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post