0

I am attempting to create a grid of locators that serve as projected points onto a parallel finite plane from a camera in maya at a specified depth. The grid should line up with a specified resolution so as to match rendered output.

At the moment my calculations are off and I am looking for some help to ascertain how my formula for ascertaining the projected points is incorrect.

I have a self contained python script and image showing the current position of locators that are spawned as an example.

image showing current spawned locators are off on y and z axis

import maya.cmds as mc
import maya.OpenMaya as om

res = [mc.getAttr('defaultResolution.width'), 
        mc.getAttr('defaultResolution.height')]

print res
grid = [5, 5]    


def projectedGridPoint(camera, coord, depth, res):


    selList = om.MSelectionList()
    selList.add(camera)
    dagPath = om.MDagPath()
    selList.getDagPath(0,dagPath)
    dagPath.extendToShape()
    camMtx = dagPath.inclusiveMatrix()

    fnCam = om.MFnCamera(dagPath)
    mFloatMtx = fnCam.projectionMatrix()
    projMtx = om.MMatrix(mFloatMtx.matrix)

    #center of camera
    eyePt = fnCam.eyePoint()

    #offset position
    z = eyePt.z - depth

    #calculated xy positions
    x = (2 * z * coord[0] / res[0]) - z
    y = (2 * z * coord[1] / res[1]) - z

    return om.MPoint(x,y,depth) * camMtx * projMtx.inverse()

for y in range(grid[1] + 1):
    for x in range(grid[0] + 1):
        coord = ( x / float(grid[0]) * res[0], y / float(grid[1]) * res[1] )
        pt = projectedGridPoint('camera1', coord, 10, res)

        mc.spaceLocator(a=1, p=[pt.x, pt.y, pt.z])
monsterAdurm
  • 63
  • 1
  • 7
  • So are the points supposed to be projected in the camera frustrum? ie, is a depth of 0 supposed to sit on the clip plane? – theodox May 03 '18 at 23:36
  • Yes, the grid points should be subdividing /lining up with the viewing area of a rendered image at a specified depth ( in this case 10 units, passed in as an arg to projectedGridPoint function ) – monsterAdurm May 04 '18 at 01:18

2 Answers2

0

I think you want something a bit more like this (note, i converted it to API 2 to cut down on the boilerplate)

    import maya.api.OpenMaya as om
    import maya.cmds as cmds


    def projectedGridPoint(camera, coord, depth):
        selList = om.MGlobal.getSelectionListByName(camera)
        dagPath = selList.getDagPath(0)
        dagPath.extendToShape()
        view = dagPath.inclusiveMatrix()

        fnCam = om.MFnCamera(dagPath)
        projection = om.MMatrix(fnCam.projectionMatrix())

        viewProj =  projection * view 


        r =    om.MPoint(coord[0],coord[1], -1 * depth)  * projection.inverse()
        return r.homogenize() * view


    xx, yy = (2, 2) 

    for y in range(yy):
        for x in range(xx):

            ndc_x =  2.0 * x  / float(xx - 1) - 1
            ndc_y =  2.0 * y  / float(yy - 1) - 1
            coord = ( ndc_x, ndc_y)
            pt = projectedGridPoint('camera1', coord,0)

            c,_ = cmds.polyCube(w = 0.1, d = 0.1, h = 0.1)
            cmds.xform(c, t = (pt[0], pt[1], pt[2]))

The coords are supplied as normalized device coordinates (from -1,-1 to 1, 1 at the corners of the view) and the depth goes from the near to far clip planes -- a depth of 1 is right on the near plane and a depth of 0 is on the far plane. I think in practice I'd lock the depth at 0 and use the clip plane setting on the camera to set the depth

edit I rationalized the original, wonky method of converting index values to NDC coordinates

theodox
  • 12,028
  • 3
  • 23
  • 36
  • This seems to work quite well, until the grid adjustments are subdivided. For instance changing this to (6, 6) and the camera is way off center. Im also wondering what the 4.0 value within the coordinate calculations are for. – monsterAdurm May 04 '18 at 02:54
0

Once I adjusted Theodox's answer to account for all possible grid divisions, such that the ndc_x and ndc_y was always in the range of -1 and 1. I was able to get a working solution.

import maya.api.OpenMaya as om
import maya.cmds as cmds


def projectedGridPoint(camera, coord, depth):
    selList = om.MGlobal.getSelectionListByName(camera)
    dagPath = selList.getDagPath(0)
    dagPath.extendToShape()
    view = dagPath.inclusiveMatrix()

    fnCam = om.MFnCamera(dagPath)
    projection = om.MMatrix(fnCam.projectionMatrix())

    viewProj =  projection * view 


    r =    om.MPoint(coord[0],coord[1], -1 * depth)  * projection.inverse()
    return r.homogenize() * view


xx, yy = (6, 6) 

for y in range(yy + 1):
    for x in range(xx + 1):
        ndc_x = -1
        ndc_y = -1

        if x > 0:
            ndc_x = (x / float(xx) * 2) - 1
        if y > 0:    
            ndc_y = (y / float(yy) * 2) - 1

        coord = ( ndc_x, ndc_y)
        print coord
        pt = projectedGridPoint('camera1', coord, 0)

        c,_ = cmds.polyCube(w = 0.1, d = 0.1, h = 0.1)
        cmds.xform(c, t = (pt[0], pt[1], pt[2]))
monsterAdurm
  • 63
  • 1
  • 7
  • I fixed the coordinate calculation, the first version happened to work for my test numbers but was incorrect – theodox May 04 '18 at 16:35