Is there a way to display a pygame window over OpenGL? - python

I've been meddling around with PyOpenGL and pygame, and I managed to create an FPS-style camera object. Now I want to add a crosshairs in the middle of the screen, and potentially expand to display statistics on the sides of the window.
I've already looked into this, and it seems like you have to do some weird stuff with OpenGL like disabling depth test and changing the projection matrix, and until now none of that actually renders anything, and reduces performance.
It seems to me that it should be very easy, as all I want is something that is over everything else, and doesn't ever move. Is there really no way to tell pygame to draw over OpenGL so I can just draw two lines in the middle of the screen?

No there is no specified way to do that. Do it in OpenGL it is not that complicate.
According to your previous questions, I assume you want to do it in immediate mode using glBegin - glEnd sequences.
In the following I assume that width is the width of the window and height its height. You have to disable the depth test and back up the current matrices by glPushMatrix/glPopMatrix. Load the Identity matrix for the model view matrix and setup an orthographic projection corresponding to the window size (glOrtho):
cross_size = 100
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0, width, height, 0, -1, 1)
glDisable(GL_DEPTH_TEST)
glColor3ub(128, 128, 128) # color of the crosshair
glBegin(GL_LINES)
glVertex2f(width/2 - cross_size/2, height/2)
glVertex2f(width/2 + cross_size/2, height/2)
glVertex2f(width/2, height/2 - cross_size/2)
glVertex2f(width/2, height/2 + cross_size/2)
glEnd()
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
Ensure that 2 dimensional texturing is disabled (glDisable(GL_TEXTURE_2D))

Related

Rendering cube with pyopengl using tkinter frame

Am trying to render cube with tkinter frame opengl.
But I don't know where the problem lies the cube didn't show expect 2 lines.
Check my code
Pls can you help me write the code and do you have any PDF to teach me opengl I can't find much resources online
import tkinter as tk
from OpenGL.GL import *
from pyopengltk import
OpenGLFrame
cubeVertices =
((1,1,1),(1,1,-1),
(1,-1,-1),(1,-1,1),.
(-1,1,1),(-1,-1,-1),
(-1,-1,1),(-1,1,-1))
cubeEdges = ((0,1),.
(0,3),(0,4),(1,2),.
(1,7),(2,5),(2,3),.
(3,6),(4,6),(4,7),.
(5,6),(5,7))
classframe(OpenGLFrame):
def initgl(self):
glViewport(0,0,250,250)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0,self.width,self.height,0,-1,1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def redraw(self):
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glPushMatrix()
glRotatef(90,0.0,1.0,0.0)
glBegin(GL_LINES)
glColor3f(0.0,1.0,0.0)
for cubeEdge in cubeEdges:
for cubeVertex in cubeEdge:
glVertex3fv(cubeVertices[cubeVertex])
glEnd()
glPopMatrix()
root = tk.Tk()
app = frame(root,width=900, height=600)
app.pack(
fill=tk.BOTH,expand=tk.YES)
app.mainloop()
You have to change the projection matrix. Since the cube has a dimension of 2x2x2 and the projection is an orthographic projection in window space, the cube will cover just 4 pixels in the window.
Change the view space and increase the distance to the near and far plane. Note, the geometry has to be in between the near and far plane, else the geometry will be clipped. For instance:
glOrtho(-10, 10, -10, 10, -10, 10)
Anyway I recommend to use Perspective projection. The projection matrix defines a 3 dimensional space (clip space) which is projected on the 2 dimensional viewport. At Perspective projection, this space is a frustum (Viewing frustum). The matrix can be set by gluPerspective. For instance:
classframe(OpenGLFrame):
def initgl(self):
glViewport(0,0,250,250)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# glOrtho(-10, 10, -10, 10, -10, 10)
gluPerspective(90, self.width/self.height, 0.1, 10.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
The value for the near plane and the far plane have to be greater than 0.0 and the far plane has to be greater then the near plane 0 < near < far. In the above example the near plane is 0.1 and the far plane 10. When you draw the geometry, the you have to ensure, that the geometry is in between then near and the far plane (in clip space respectively in the viewing volume), else the geometry is clipped.
Use gluLookAt to define a view matrix with a point of view (0, -3, 0) that has a certain distance to the origin of the world (0, 0, 0):
classframe(OpenGLFrame):
# [...]
def redraw(self):
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
# define view matrix
glLoadIdentity()
gluLookAt(0, -3, 0, 0, 0, 0, 0, 0, 1)
glPushMatrix()
glRotatef(90,0.0,1.0,0.0)
glBegin(GL_LINES)
glColor3f(0.0,1.0,0.0)
for cubeEdge in cubeEdges:
for cubeVertex in cubeEdge:
glVertex3fv(cubeVertices[cubeVertex])
glEnd()
glPopMatrix()
What exactly are eye space coordinates?

How to use multiple glViewport() and glOrtho()

I am trying to use pygame and pyopengl, in the main window i have 2 viewports
1 big map and 1 minimap (both presenting the same frame). i need both maps to rotate around a center who isnt 0,0,0 (lets say i need the center of rotation to be -130,0,60 which needs to be a constant point)
also i need 1 view to view a distance of glTranslatef(0, 0, -1000)
and the 2nd view to be glTranslatef(1, 1, -200) both distances are constant
i tried to use
gluLookAt()
glOrtho()
but it doesnt change the rotation.... around 0,0,0
or i might be using it wrong.
the code looks like this:
pygame.init()
display = (1700, 1000)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
gluPerspective(50, (display[0] / display[1]), 0.1, 5000)
glTranslatef(0, 0, -1000) # this is the view distance i want from map 1
while True:
##### i use this function to zoom in and out with mouse Wheel
##### also the zoom in/out zooms to 0,0,0 and i need (-130,0,60)
if move_camera_distance:
if zoom_in:
glScalef(0.8,0.8,0.8)
elif zoom_out:
glScalef(1.2, 1.2, 1.2)
move_camera_distance = False
zoom_in = False
zoom_out = False
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
###### Map 1
###### Need to rotate around (-130,0,60)
###### distance from camera need to be (0,0,-1000)
glViewport(1, 1, display[0], display[1]) # splits the screen
glCallList(obj.gl_list)
DrawBuffer(bufferObj, noPoints, noCirclePoints, noCrossPoints)
###### Map 2
###### Need to rotate around (-130,0,60)
###### distance from camera need to be (0,0,-300)
glViewport(1300, 650, 400, 400) # splits the screen
glCallList(obj.gl_list)
DrawBuffer(bufferObj, noPoints, noCirclePoints, noCrossPoints)
pygame.display.flip()
pygame.time.wait(10)
The output i get is 2 maps, both rotate around 0,0,0 both are from a distance of (0,0,-1000) and both change together if i change anything in the While loop.
thanks for help.
Note that the current matrix can be stored to the matrix stack by glPushMatrix and restored from the matrix stack by glPopMatrix. There are different matrix modes and matrix stacks (e.g. GL_MODELVIEW, GL_PROJECTION). See (glMatrixMode).
Set the projection matrix at initialization and set different modelview matrices for the different views:
glMatrxMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(50, (display[0] / display[1]), 0.1, 5000)
glMatrxMode(GL_MODELVIEW)
glLoadIdentity()
# [...]
while True:
###### Map 1
glViewport(1, 1, display[0], display[1]) # splits the screen
glPushMatrix()
glTranslatef(0, 0, -1000) # this is the view distance i want from map 1
# [...]
glPopMatrix()
###### Map 2
glViewport(1300, 650, 400, 400) # splits the screen
glPushMatrix()
glTranslatef(0, 0, -300) # this is the view distance i want from map 1
# [...]
glPopMatrix()
To rotate the model around a point, you've to:
Translate the model in that way that way, that the pivot is in the origin of the world. This is a translation by the inverse pivot vector.
Rotate the model.
Translate the rectangle in that way that the pivot is back at its original position.
In the program, this operations have t o be applied in revers order, because operations like glTranslate and glRotate define a matrix and multiply the matrix to the current matrix:
glTranslatef(-130, 0, 60);
glRotate( ... )
glTranslatef(130, 0, -60);
Do this immediately before you draw the object.

PyOpenGL, issue with 3D graphics on a screen using glFrustum and glTranslate

Overview:
I am trying to create a 3D application similar to this:
www.youtube.com/watch?v=h9kPI7_vhAU.
I am using OpenCV2.2, Python2.7 and pyOpenGL.
This can be achieved by this background maths and code snippet where x, y, z are the positions of the viewers eye (as grabbed from a webcam!)
Issue:
When I do this, the object (a cube) that I have rendered becomes stretched along the z axis (into the screen) and I'm not too sure why. It is likened to looking down a very tall skyscraper from above (as opposed to a cube). The cube's position changes very rapidly in the z direction as the z position of the eye changes. This is a frame of the result, it has been stretched!
Code (with bigD's edit):
def DrawGLScene():
#get some parameters for calculating the FRUSTUM
NEAR_CLIPPING_PLANE = 0.01
FAR_CLIPPING_PLANE = 2
window = glGetIntegerv(GL_VIEWPORT)
WINDOW_WIDTH = window[2]
WINDOW_HEIGHT= window[3]
#do facial detection and get eye co-ordinates
eye = getEye()
#clear window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#before any projection transformation command comes these 2 lines:
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
#transform projection to that of our eye
glFrustum(NEAR_CLIPPING_PLANE*(-WINDOW_WIDTH /2 - eye[0])/eye[2],
NEAR_CLIPPING_PLANE*( WINDOW_WIDTH /2 - eye[0])/eye[2],
NEAR_CLIPPING_PLANE*(-WINDOW_HEIGHT/2 - eye[1])/eye[2],
NEAR_CLIPPING_PLANE*( WINDOW_HEIGHT/2 - eye[1])/eye[2],
NEAR_CLIPPING_PLANE, FAR_CLIPPING_PLANE)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(-eye[0],-eye[1],-eye[2])
drawCube()
glutSwapBuffers()
an example of the data getEye() returns is:
[0.25,0.37,1] if viewers is has their face near lower left of screen and is 1m away
[-0.5,-0.1,0.5] if viewers is has their face near upper right of screen and is 0.5m away
The cube when drawn has height, width, depth of 2 and its centre at (0,0,0).
I will provide the full code if anyone wants to do a similar project and wants a kickstart or thinks that the issue lies somewhere else than code provided.
The reason why you're getting strange results is because of this:
glTranslatef(-eye[0],-eye[1],-eye[2])
This call should be made after
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
Because the projection matrix is ready as it is with your glFrustum call, if you multiply it by a translation matrix that won't make it a perspective projection matrix anymore. The modelview matrix has to describe all world AND camera transformations.
Also bear in mind that if the only transformation you do on your modelview matrix is a translation, then you will always be staring down the negative-Z axis.

PIL Image.fromstring from PyOpengl buffer has the wrong size

I use PyOpenGL to draw a 2D Image. Then I want to use the Python Imaging Library (PIL) to store this image to disk. I use GLUT to display the image which works perfectly. But when I use PIL to store the image it extracts the wrong clipping. It has the wrong size.
Here is a minimal example which reproduces the effect and I also attach the output to make it more clear without running some code.
from OpenGL.GL import *
from OpenGL.GLUT import *
from PIL import Image
width, height = 640, 480
def DrawStuff():
poly1 = [(0,0), (640,0), (0,480)]
color = (0.5, 0.4, 0.3, 0.8)
glClear(GL_COLOR_BUFFER_BIT)
glPushMatrix()
glLineWidth(5.0)
glColor4f(*color)
glBegin(GL_POLYGON)
glVertex2f(poly1[0][0], poly1[0][1])
glVertex2f(poly1[1][0], poly1[1][1])
glVertex2f(poly1[2][0], poly1[2][1])
glVertex2f(poly1[0][0], poly1[0][1])
glEnd() # GL_POLYGON
glPopMatrix()
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.fromstring("RGBA", (width, height), data)
image.show()
image.save('out.png', 'PNG')
glutSwapBuffers()
# glut initialization
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA)
glutCreateWindow("Draw Polygons")
glutInitWindowSize(width, height)
# set the function to draw
glutDisplayFunc(DrawStuff)
# enable the alpha blending
glEnable(GL_BLEND)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
# prepare for 2D drawing
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, height, 0, 0, 1)
glDisable(GL_DEPTH_TEST)
glMatrixMode(GL_MODELVIEW)
# start the mainloop
glutMainLoop ()
this is how it looks int the GLUT window and how it is supposed to look like
and this is how the saved image looks like
I managed to solve my own Problem.
First I tried the following solution which might also help people with related problems:
solution1
But then, through extensive trial and error, I found that the solution is much simpler.
I simply had to swap two lines from:
glutCreateWindow("Draw Polygons")
glutInitWindowSize(width, height)
to
glutInitWindowSize(width, height)
glutCreateWindow("Draw Polygons")
Apparently the size has to be set before the window
You should consider that in OpenGL the coordinate system starts at different place than in PIL. Look at this.

coordinates changed when migrating from pygame+rabbyt to pyglet+rabbyt

I'm working on a 2D game and decided to switch from SDL to OpenGL. I took rabbyt as an opengl wrapper for rendering my sprites and using pymunk (chipmunk) for my physics. I used pygame for creating the window and rabbyt for drawing the sprites on the screen.
I discovered that with pygame+rabbyt the (0,0) coordinate is in the middle of the screen. I liked that fact, because the coordinate representation in the physics engine were the same as in my graphics engine (I don't have to recalculate the coordinates when rendering the sprites).
Then I switched to pyglet because I wanted to draw lines with OpenGL - and discovered that suddenly the (0,0) coordinate was at the bottom left of the screen.
I suspected that that has something to do with the glViewport function, but only rabbyt executes that function, pyglet touches it only when the window is resized.
How can I set the (0,0) coordinate at the middle of the Screen?
I'm not very familiar with OpenGL and couldn't find anything after several hours googling and trial&error... I hope someone can help me :)
Edit: Some additional information :)
This is my pyglet screen initialization code:
self.window = Window(width=800, height=600)
rabbyt.set_viewport((800,600))
rabbyt.set_default_attribs()
This is my pygame screen initialization code:
display = pygame.display.set_mode((800,600), \
pygame.OPENGL | pygame.DOUBLEBUF)
rabbyt.set_viewport((800, 600))
rabbyt.set_default_attribs()
Edit 2: I looked at the sources of pyglet and pygame and didn't discover anything in the screen initialization code that has something to do with the OpenGL viewport... Here is the source of the two rabbyt functions:
def set_viewport(viewport, projection=None):
"""
``set_viewport(viewport, [projection])``
Sets how coordinates map to the screen.
``viewport`` gives the screen coordinates that will be drawn to. It
should be in either the form ``(width, height)`` or
``(left, top, right, bottom)``
``projection`` gives the sprite coordinates that will be mapped to the
screen coordinates given by ``viewport``. It too should be in one of the
two forms accepted by ``viewport``. If ``projection`` is not given, it
will default to the width and height of ``viewport``. If only the width
and height are given, ``(0, 0)`` will be the center point.
"""
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
if len(viewport) == 4:
l, t, r, b = viewport
else:
l, t = 0, 0
r, b = viewport
for i in (l,t,r,b):
if i < 0:
raise ValueError("Viewport values cannot be negative")
glViewport(l, t, r-l, b-t)
if projection is not None:
if len(projection) == 4:
l, t, r, b = projection
else:
w,h = projection
l, r, t, b = -w/2, w/2, -h/2, h/2
else:
w,h = r-l, b-t
l, r, b, t = -w/2, w/2, -h/2, h/2
glOrtho(l, r, b, t, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def set_default_attribs():
"""
``set_default_attribs()``
Sets a few of the OpenGL attributes that sprites expect.
Unless you know what you are doing, you should call this at least once
before rendering any sprites. (It is called automatically in
``rabbyt.init_display()``)
"""
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
glEnable(GL_BLEND)
#glEnable(GL_POLYGON_SMOOTH)
Thanks,
Steffen
As l33tnerd suggested the origin can be placed at the center with glTranslatef...
I added the following below my screen initialization code:
pyglet.gl.glTranslatef(width/2, height/2, 0)
Thanks!

Categories