1
votes

I'm attempting to write a program which takes in a x,y,z coordinates for a set of atoms in a protein, which have all been normalized to the range [0,1]. However, in doing so, I'm having immense difficulty drawing the points so that they appear "three-dimensionally." That is, I can graph the points without much trouble using glVertex3fv and providing all three coordinates for each point, but my attempts to use gluPerspective and gluLookAt in order to frame the camera appropriately have been dismal, and I haven't been able to find any resources online explaining why.

I've specified the arguments for gluPerspective to use a 50.0 field of view angle, as well as a 1:1 aspect ratio. I've specified the minZ and maxZ arguments to be -1 and 1, which should encompass the breadth of my data points, as they're normalized from [0,1] as mentioned. Commenting out gluPerspective shows the points graphed in "2d" space -- although, if my understanding is correct, they are drawn in three dimensions. Uncommenting gluPerspective renders the window blank.

gluLookAt seems to do absolutely nothing, no matter what arguments I pass in.

I'm using PyOpenGL, which is just a wrapper for OpenGL for Python. This allows me to use both OpenGL and some python-specific libraries -- in this case, ProDy (a library for parsing/fetching/manipulating protein data files.)

I'm using Python 2.7 and OpenGL 3.1 build 9.17.10.3517.

My program fetches a protein file from the PDB, parses it, gets the coordinates for each atom within the protein, normalizes them, and graphs the coordinates to the screen.

Here's my code:

import OpenGL 
from prody import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

#create initial window parameters
width = 500
height = 500
#fetch PDB data
id = "1cwa"
filename = fetchPDB(id)
atoms = parsePDB(filename)
coords = atoms.getCoords()

#max/mins

class Stats(object):
    maxX = coords[0][0]
    maxY = coords[0][1]
    maxZ = coords[0][2]
    minX = maxX-1
    minY = maxY-1
    minZ = maxZ-1
    meanX = 0
    meanY = 0
    meanZ = 0

def Scale():
#find max and min for each range
    for a in coords:
        #print(a[2])
        if(a[0] < Stats.minX):
            Stats.minX = a[0]
        elif(a[0] > Stats.maxX):
            Stats.maxX = a[0]
        if(a[1] < Stats.minY):
            Stats.minY = a[1]
        elif(a[1] > Stats.maxY):
            Stats.maxY = a[1]
        if(a[2] < Stats.minZ):
            Stats.minZ = a[2]
        elif(a[2] > Stats.maxZ):
            Stats.maxZ = a[2]
    #multiply projection matrix
    quoX = Stats.maxX - Stats.minX
    quoY = Stats.maxY - Stats.minY
    quoZ = Stats.maxZ - Stats.minZ
    quoN = max(quoZ,max(quoX,quoY))
    Stats.meanX = (Stats.maxX + Stats.minX)/2
    Stats.meanY = (Stats.maxY + Stats.minY)/2
    Stats.meanZ = (Stats.maxZ + Stats.minZ)/2
    for a in coords:
        a[0] = (a[0]-Stats.minX)/(quoN)
        print(a[0])
        a[1] = (a[1]-Stats.minY)/(quoN)
        print(a[1])
        a[2] = (a[2]-Stats.minZ)/(quoN)
        print(a[2])


def drawAtom(atom):
    x = atom[0]
    y = atom[1]
    z = atom[2]
    glBegin(GL_POINTS)
    #glVertex2f(x,y)
    glVertex3fv(atom)
    glEnd()  

def draw():
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glEnable(GL_DEPTH_TEST)
    glPointSize(7)
    for atom in coords:
        drawAtom(atom)
        glutSwapBuffers()

def init():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)
    glutInitWindowPosition(0, 0)
    glutInitWindowSize(width,height)
    glutCreateWindow(b"CycloVis")
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_DEPTH_TEST)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    Scale()
    #gluPerspective(50.0,1.0,-1,1)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    gluLookAt(1,1,5,Stats.minZ,Stats.meanZ,Stats.maxZ,9999999,1,1)
    glColor3d(1.0, 0.0, 1.0)
    glutDisplayFunc(draw)

if __name__ == '__main__':

#initialize
    init()

    if "-vers" in sys.argv:
        print ("GL_VERSION    = ", glGetString(GL_VERSION))

    glutMainLoop()
1
You're absolutely right. I suppose I just got the two mixed up as I was writing my question. I'll edit it now for clarity's sake.Daniel Garrick
gluLookAt takes 3 vectors as its parameters, I'm not sure where your values are coming from. But the first triplet should be the camera's origin, the second should be the center (point you are focusing on) and the last should be a vector that points up. What is your interest in gluLookAt? Are you just trying to fit all of these data points in your viewport? Generally you would use gluLookAt if you wanted to rotate the scene and position the camera somewhere.Andon M. Coleman
The values are pretty much hard-coded haberdashery. However, my frustration lies more in the fact that no matter what arguments I pass in to gluLookAt (no matter how absurd or large), nothing happens. It seems as if I'm fundamentally misunderstanding how the method works. My interest in gluLookAt is, indeed, to fit all of the data points in the viewport, regardless of what protein is being examined, in such a way that the points are displayed from an angle that accurately shows the points with respect to three dimensions, rather than a straight-on graph-like view.Daniel Garrick

1 Answers

0
votes

gluLookAt is not going to rescale your viewing volume. It will translate and rotate all of your points, but to scale everything you need a projection matrix. When you comment out your call to gluPerspective, the viewing volume extends from [-1,1] in all directions (because the projection matrix is an identity matrix). I imagine you have discovered that when you comment out both gluPerspective and gluLookAt that your set of normalized data points ([0,1]) fills about 1/4 of your viewport (top-right corner)?

Now, as for gluLookAt messing things up, that is also easy to explain. The code you showed in your question uses the aforementioned [-1,1] viewing volume (Normalized Device Coordinates). gluLookAt will rotate and translate your points, but they still have to all lie within +/- 1 unit of your "eye point" (first three coordinates passed to gluLookAt) to be visible. That is absolutely not the case when you use 1,1,5 as your eye point. None of your normalized data points lie in the range z=[4,6].

This will probably produce better results for your data set:

gluLookAt(0.5,0.5,0.0, 0.5,0.5,1.0, 0.0,1.0,0.0)

That centers your camera smack dab in the middle of the [0,1] X,Y range and positions the nearest point at the near clipping plane. It looks down the positive Z-axis and has the Y-axis pointing up.