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!

Cheers,

Cerac

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.

Cheers,
Maxime

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.

[c4d.CAMERAOBJECT_FILM_OFFSET_Y]``` [c4d.CAMERAOBJECT_FILM_OFFSET_X]`

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)  
        
      else:  
          pass

Thanks again!

Cerac

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)
    
    c4d.EventAdd()

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.
Cheers,
Maxime

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  
      try:     
          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]  
  
          doc.SetActiveRenderData(rd)  
          c4d.EventAdd()  
        
      except ZeroDivisionError:  
          pass  
  else:  
      pass  

Thanks again,

Cerac