Search code examples
pythonopenglpyopengl

pyopengl - finding the edges of a shape during rotation


The idea behind this is to create a detection area for a security camera. Currently, I know how to find and use the modelview matrix data as shown below in the function "matrixTransformation". The value for the matrix should then be calculated for each increase of rotation of the security camera in the initialization function.

I would like to know how you would find coordinates of the edges of each security camera, a cylinder shape, using the matrix. I am using Pygame 1.9.2, Python 3.5 and PyOpenGL-3.1.0.

Picture of coordinates on the security camera which need to be calculated

def matrixTransformation(x,y,z):

    matrix = (GLfloat * 16)()
    glGetFloatv(GL_MODELVIEW_MATRIX, matrix)

    xp = matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12]
    yp = matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13]
    zp = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14]
    wp = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15]

    xp /= wp
    yp /= wp
    zp /= wp

    return xp,yp,zp

def init():

    securityCameraRotation=380

    glEnable(GL_DEPTH_TEST)

    multipleRotations=0
    result=[]

    glPushMatrix()

    glTranslatef(-4,1.5,5.5)
    glRotate(315,1,1,1)

    while True:
        if securityCameraRotation>=380:
            clockwise=True
            multipleRotations+=1
        elif securityCameraRotation<=310:
            clockwise=False
        glRotate(securityCameraRotation,0,1,0)
        #append the transformed coordinates to result

        if clockwise==True:
            securityCameraRotation-=0.2
        elif clockwise==False:
            securityCameraRotation+=0.2
        if multipleRotations>1:
            #End the loop when one complete rotation between 310 and 380 has occured
            break

        glPopMatrix()

        return result

def securityCamera(radius, height, num_slices,frontCircleColour,backCircleColour,tubeColour):
    r = radius
    h = height
    n = float(num_slices)

    circle_pts = []
    for i in range(int(n) + 1):
        angle = 2 * math.pi * (i/n)
        x = r * math.cos(angle)
        y = r * math.sin(angle)
        pt = (x, y)
        circle_pts.append(pt)

    glBegin(GL_TRIANGLE_FAN) #drawing the back circle
    glColor(backCircleColour)
    glVertex(0, 0, h/2.0)
    for (x, y) in circle_pts:
        z = h/2.0
        glVertex(x, y, z)
    glEnd()

    glBegin(GL_TRIANGLE_FAN) #drawing the front circle
    glColor(frontCircleColour)
    glVertex(0, 0, h/2.0)
    for (x, y) in circle_pts:
        z = -h/2.0
        glVertex(x, y, z)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP) #draw the tube
    glColor(tubeColour)
    for (x, y) in circle_pts:
        z = h/2.0
        glVertex(x, y, z)
        glVertex(x, y, -z)
    glEnd()

Solution

  • In OpenGL, there are a bunch of transformations that occur. First, we treat the object as if it is in model space, where the the object is centered at the origin and we draw the mesh (in this case, the cylinder). Then, we apply a model matrix transform (where we translate/rotate/scale our cylinder) and a view matrix transform (where we shift our scene relative to the imaginary camera). Finally we apply the projection matrix that adds the "3d perspective" to our scene by creating a matrix with gluPerspective or some more modern means. All of these matrix multiplications basically put the coordinates of your 3d models in the right place on our 2d screens (sort of, more detailed info here).

    In terms of the model space, the yellow points you highlighted in your picture are actually just (0, 0, -h/2.0) and (0, 0, h/2.0). This is fine if you are just drawing your yellow points with glBegin(GL_POINTS) in your securityCamera function. However, you are probably more interested in calculating where these yellow points are located in world space (that is, after multiplication by the modelview matrix).

    One simple way to get these world space coordinates is to multiply the yellow points' model space coordinates by the modelview matrix. Use your matrixTransformation function on (0, 0, -h/2.0) and (0, 0, h/2.0) and that should work!

    Alternatively, as I hinted at in the comments, matrices like your modelview matrix are actually contain useful information that results from the accumulation of multiplications of translation, rotation, and scaling matrices. I pointed to this picture:

    enter image description here

    Each of these column axes actually corresponds to the rows of your numpy array (which is interesting since numpy is row-major while OpenGL is column-major). You can get the following axes of how your model is pointing in world spaces with the following snippet:

    mv_matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
    left, up, forward, position = [v/(np.linalg.norm(v)) for v in mv_matrix[:,:3]]
    

    Note that I cut off the last row in the numpy array and normalized each of the axes. If you take the forward array you get from there, you get the direction in world space of where that particular camera is pointing, while the position array gives you the world space position of the center (model space equivalent of (0, 0, 0)) of the security camera. Multiply the normalized forward array by h/2.0 and add that to position and you should get the world space position of the front of your security camera. This is not too useful for rendering to the screen, but could be used for "behind the scenes" math for interesting objects with the security camera.

    As a side note, I realized I made a sign error in this line for the cylinder drawing code:

    glBegin(GL_TRIANGLE_FAN) #drawing the front circle
    glColor(frontCircleColour)
    glVertex(0, 0, -h/2.0)#this was + in the original code!!!
    

    Let me know if this helps you make sense of my earlier comments!