convert world coordinates to camera (render) coordinates?

I’m writing a script to automatically crop a render to certain objects (using blender’s border functionality). I’m using the getBoundBox() function to get the bounds of the object. However, I’m having a hard time translating camera coordinates into render coordinates to give blender the correct borders to render with. My code is below. I based it on this very helpful post. Right now the border positions change depending on how much the user is zoomed in / panned over. I noticed that using getViewMatrix() instead of getPerspectiveMatrix() gives values that don’t change with view/pan, but I still don’t know how to translate to render coordinates.


#dehomogenizes a vector
def dehom(v):
    ret = Vector(v[0]/v[3],v[1]/v[3],v[2]/v[3])
    return ret
    
    
def cropToObject(ob,cam):
    print "crop to object"
    cam.select(1)
    Window.CameraView()
    
    pm = Window.GetPerspMatrix()
    #pm = Window.GetViewMatrix()
    coords = ob.getBoundBox()
    
    x, y, x2, y2 = 1.0, 1.0, 0, 0
    #order is important!
    for c in coords:
        v = Vector(c)
        v.resize4D()
        v = v * pm
        v = dehom(v)
        v += Vector(0.5,0.5,0.5)
        print v
        if v.x < x: x = v.x
        if v.y < y: y = v.y
        if v.x > x2: x2 = v.x
        if v.y > y2: y2 = v.y
    
    print 'coords',x,y,x2,y2
    return [x, y, x2, y2]

thanks in advance,
bydesign

Well sadly, no one was brave enough to reply, but since I had to get this done I found the solution in the sflender script. It was a little complicated, and since my version is a little shorter, and nobody had this on the forums, I figured I’d post it.

So in summary, there are two functions that are desperately needed in the Blender Python API. The functions below (Perspective, Ortho) should be reproduced in blender and be able to receive a camera object, and return a perspective or orthographic matrix.

Right now my cropToObject() function is calling those other functions and converting the results to allow me to find the border around an object so that I can automatically crop the render to that object. This function also returns a two-dimensional array containing the x/y position of the upper left corner of the object in the render. Hope this helps anyone trying to map global world locations to two-dimensional (2d to 3d) camera / render coordinates.


def cropToObject(ob,camera):
    print "crop to object"
    coords = ob.getBoundBox()
    
    context = SCENE.getRenderingContext()
    w = context.sizeX
    h = context.sizeY
    ax = context.aspectX
    ay = context.aspectY
    
    mW = w/2
    mH = h/2
    
    cam = camera.getInverseMatrix()
    cam.transpose()
    cmra = camera.getData()
     
    fovy = atan(0.5/(float(w*ax)/float(h*ay))/(cmra.lens/32))
    fovy = fovy *360/pi
    if cmra.type == 'ortho':
        m2 = Ortho(fovy,float(w*ax)/float(h*ay),cmra.clipStart, cmra.clipEnd,17) #cmra.scale)
    else:
        m2 = Perspective(fovy,float(w*ax)/float(h*ay),cmra.clipStart, cmra.clipEnd)
        
    mP = m2 * cam
    
    xPos,yPos = w,0
    x1, y1, x2, y2 = 1.0, 1.0, 0, 0
    for p in coords:
        p.resize4D()
        p = mP * p
        
        x = int((p[0]/p[3])*mW)+mW
        y = int((p[1]/p[3])*mH)+mH
        
        if x < xPos: xPos = x
        if y > yPos: yPos = y
            
        v = Vector(x*1.0/w,y*1.0/h,0)
        if v.x < x1: x1 = v.x
        if v.y < y1: y1 = v.y
        if v.x > x2: x2 = v.x
        if v.y > y2: y2 = v.y
            
    if xPos < 0: xPos = 0
    if yPos < 0: yPos = 0
        
    return [x1, y1, x2, y2],[w-xPos,h-yPos]


# ---------------------------------------------------------------
#     Function    : Perspective
#    Inputs        :
#     OutPuts     :
#    Description    :
# ---------------------------------------------------------------    
def Perspective(fovy, aspect, near,far):
    top = near * tan(fovy * pi / 360.0)
    bottom = -top
    left = bottom*aspect
    right= top*aspect
    x = (2.0 * near) / (right-left)
    y = (2.0 * near) / (top-bottom)
    a = (right+left) / (right-left)
    b = (top+bottom) / (top - bottom)
    c = - ((far+near) / (far-near))
    d = - ((2*far*near)/(far-near))
    return Matrix([x,0.0,a,0.0],[0.0,y,b,0.0],[0.0,0.0,c,d],[0.0,0.0,-1.0,0.0])
        
# ---------------------------------------------------------------
#     Function    f: Ortho
#    Inputs        :
#     OutPuts     :
#    Description    :
# ---------------------------------------------------------------            
def Ortho(fovy, aspect ,near,far,scale):
    top = near * tan(fovy * pi / 360.0) * (scale * 10)
    bottom = -top 
    left = bottom * aspect
    right= top * aspect
    rl = right-left
    tb = top-bottom
    fn = near-far 
    tx = -((right+left)/rl)
    ty = -((top+bottom)/tb)
    tz = ((far+near)/fn)
    return Matrix([2.0/rl,0.0,0.0,tx],[0.0,2.0/tb,0.0,ty],[0.0,0.0,2.0/fn,tz],[0.0,0.0,0.0,1.0])