Convert 3D space to 2D Space

  • On 04/07/2018 at 03:36, xxxxxxxx wrote:

    Hey Everyone,

    Just wanted to know if anyone can point me in the right direction.

    Im looking to convert a 3D (world) space, point co-ordinate into a 2D camera space co-ordinate.

    Is this possible with the python SDK?

    Any info or a nudge in the right direction is much appreciated!



  • On 04/07/2018 at 12:22, xxxxxxxx wrote:

    Check out the BaseView class
    BaseView::WC() or BaseView::WC_V() is what you most likely are looking for.

  • On 05/07/2018 at 02:12, xxxxxxxx wrote:

    Hi Ceracticus,

    As Daniel already pointed you BaseView.WC and BaseView.WC_V is the way to go.

    Additionally, I would like to point you to the C++ manual which can give you some valuable information even for python.


  • On 05/07/2018 at 09:53, xxxxxxxx wrote:

    Hey Both,

    thanks so much for your suggestions, massively helpful.

    Managed to get 90% of the way with looking up other examples on this site, plus your feedback which is really great.

    What im essentially doing is rebuilding the interactive render region crop function out of guides (converted from world to screen space) then plugging these values into the active render setting.

    Ive got everything working as expected - apart from when a camera has film offset. I know i can access this data which is great.


    Does anyone know what the best way of taking this into account whilst running my code?

    def main() :  
      rd = doc.GetActiveRenderData()  
      rd_w = rd[c4d.RDATA_XRES_VIRTUAL]  
      rd_h = rd[c4d.RDATA_YRES_VIRTUAL]  
      bd = doc.GetActiveBaseDraw()       # Get current view  
      sf = bd.GetSafeFrame() #SafeFrame  
      obj = op.GetObject()               # Get Null  
      cam = obj[c4d.ID_USERDATA,2]       # Get Camera  
      top = obj[c4d.ID_USERDATA,3]       # GetGuideObjects  
      bottom = obj[c4d.ID_USERDATA,7]  
      left = obj[c4d.ID_USERDATA,8]  
      right = obj[c4d.ID_USERDATA,9]  
      switch = obj[c4d.ID_USERDATA,4]           # Get Switch  
      cZ = cam[c4d.CAMERAOBJECT_TARGETDISTANCE] #z-distance  
      if switch:  
          # +-------------------------- TOP IRR  
          topPos = top.GetMg().off #top global position  
          top_v = bd.WS(topPos) #World2Screen  
          top_v[0] = (top_v[0] - sf["cl"])/(sf["cr"]-sf["cl"])  
          top_v[1] = (top_v[1] - sf["ct"])/(sf["cb"]-sf["ct"])  
          top_v[2] = (cZ-top_v[2])/cZ  
          top_pos = top_v[1] #Gets Y pos from vector  
          # Rangemap the result to IRR space  
          irrTop_min = c4d.utils.RangeMap(top_pos, 0, 1, 0, rd_h, True)  
          irrTop_max = 0  
          # If its over halfway - reverse it  
          if irrTop_min >= (rd_h/2) :  
              irrTop_max = c4d.utils.RangeMap(irrTop_min, rd_h, rd_h/2, 0, rd_h/2,True)   
              rd[c4d.RDATA_RENDERREGION_TOP] = int(irrTop_max)  

    Thanks again!


  • On 09/07/2018 at 07:09, xxxxxxxx wrote:

    Hi Ceracticus,

    Actually, WS already take care of the film offset, so you just have to do something like that.

        rd = doc.GetActiveRenderData()
        rd_w = rd[c4d.RDATA_XRES_VIRTUAL]
        rd_h = rd[c4d.RDATA_YRES_VIRTUAL]
        bd = doc.GetActiveBaseDraw()
        viewX = bd.GetFrame()["cr"]
        viewY = bd.GetFrame()["cb"]
        sf = bd.GetSafeFrame() #SafeFrame
        cam = bd.GetSceneCamera(doc)
        top = doc.GetFirstObject().GetMg().off
        bot = doc.GetFirstObject().GetNext().GetMg().off
        left = doc.GetFirstObject().GetNext().GetNext().GetMg().off
        right = doc.GetFirstObject().GetNext().GetNext().GetNext().GetMg().off
    	# Get pixels counts between top obj and the frame, then scale this pixel count according to the ratio between viewport size and render size.
        top_p = max(0, bd.WS(top).y - sf["ct"]) * rd_h / viewY 
        bot_p = max(0, sf["cb"] - bd.WS(bot).y) * rd_h / viewY
        left_p = max(0, bd.WS(left).x - sf["cl"]) * rd_w / viewX
        right_p = max(0, sf["cr"] - bd.WS(right).x) * rd_w / viewX
        rd[c4d.RDATA_RENDERREGION_TOP] = int(top_p)
        rd[c4d.RDATA_RENDERREGION_BOTTOM] = int(bot_p)
        rd[c4d.RDATA_RENDERREGION_LEFT] = int(left_p)
        rd[c4d.RDATA_RENDERREGION_RIGHT] = int(right_p)

    On a side topic, please use [ code] [ /code] to insert code in your post, it increases the readability of them.

    If you have any question, please let me know.

  • On 11/07/2018 at 01:05, xxxxxxxx wrote:

    Thanks Maxime,

    i got there in the end, my solution was far less elegant than yours though, so thank you for spending the time to look through my code, i can certainly learn lots from your approach.

    Apologies also for my scrappy code - there was parts in there that made no sense whatsoever so kudos for managing to read through that.

    I'll post my solution below for reference but anyone who references this thread should definitely go with Maximes above suggestion.

    def screenCalc(guide, cZ, bd, sf, rd_w, rd_h) :  
      gPos = guide.GetMg().off  # get global position of guide  
      convertPos = bd.WS(gPos)  # convert world to screen space  
      # Perform safe zone maths  
      convertPos[0] = (convertPos[0] - sf["cl"])/(sf["cr"]-sf["cl"])  
      convertPos[1] = (convertPos[1] - sf["ct"])/(sf["cb"]-sf["ct"])  
      convertPos[2] = (cZ - convertPos[2])/cZ    
      gV1 = convertPos[0]  # get x from vector  
      gV2 = convertPos[1]  # get y from vector  
      # Remaps vector values to screenspace values, aswell as inverts  
      g_Xpos = c4d.utils.RangeMap(gV1, 0, 1, 0, rd_w, True)  
      INVg_Xpos = c4d.utils.RangeMap(gV1, 1, 0, 0, rd_w, True)  
      g_Ypos = c4d.utils.RangeMap(gV2, 0, 1, 0, rd_h, True)  
      INVg_Ypos = c4d.utils.RangeMap(gV2, 1, 0, 0, rd_h, True)  
      # Loads final values into a list and returns it  
      pos = [int(g_Xpos), int(INVg_Xpos), int(g_Ypos), int(INVg_Ypos)]  
      return pos  
    def main() :  
      # +------- GET DOCUMENT DATA --------#  
      bd = doc.GetActiveBaseDraw()                 # Get current view  
      rd = doc.GetActiveRenderData()               # Get Render Setting  
      rd_w = rd[c4d.RDATA_XRES_VIRTUAL]            # Get X resolution  
      rd_h = rd[c4d.RDATA_YRES_VIRTUAL]            # Get Y resolution  
      sf = bd.GetSafeFrame()                       # Get SafeFrame dims  
      # +------- GET CUSTOM DATA --------#  
      obj = op.GetObject()                         # Get Null  
      cam = obj[c4d.ID_USERDATA,2]                 # Get Camera  
      top = obj[c4d.ID_USERDATA,3]                 # GetGuideObjects  
      bottom = obj[c4d.ID_USERDATA,7]              # --------------  
      left = obj[c4d.ID_USERDATA,8]                # --------------  
      right = obj[c4d.ID_USERDATA,9]               # --------------  
      switch = obj[c4d.ID_USERDATA,4]              # Get Switch  
      cZ = cam[c4d.CAMERAOBJECT_TARGETDISTANCE]    # Get z-distance  
      # If tag is activated  
      if switch:  
          # Use function to grab data  
              topPos = screenCalc(top, cZ, bd, sf, rd_w, rd_h)  
              bottomPos = screenCalc(bottom, cZ, bd, sf, rd_w, rd_h)  
              leftPos = screenCalc(left, cZ, bd, sf, rd_w, rd_h)  
              rightPos = screenCalc(right, cZ, bd, sf, rd_w, rd_h)  
              # SET RENDER DATA  
              rd[c4d.RDATA_RENDERREGION_TOP] = topPos[2]  
              rd[c4d.RDATA_RENDERREGION_BOTTOM] = bottomPos[3]  
              rd[c4d.RDATA_RENDERREGION_LEFT] = leftPos[0]  
              rd[c4d.RDATA_RENDERREGION_RIGHT] = rightPos[1]  
          except ZeroDivisionError:  

    Thanks again,


Log in to reply