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!
Related
Background
I've been toying around with pyglet when I stumbled across this guy's Minecraft clone. Github is here: https://github.com/fogleman/Minecraft
I've made some modifications (for Python 3 and some of my own preferences), and the complete code is here: modified minecraft.
The Problem
Whenver I run the code, it may sometimes not register any mouse movements or key presses. It is rare, but it can happen occasionally. I would say that out of 10 times, it'll break once.
Details
I don't even know what the culprit is, but I'll provide some snippets code.
It's unpredictable, but there are some ways to fix it. The only sure-fire way currently is to FORCE QUIT (not just quit) the application and then restart it.
I'm not sure why, and I've tried all sorts of things to try and fix it.
If it matters, I'm using macOS Mojave, Python 3.8.2, and Pyglet 1.5.14
Here's the __init__ function for the window:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Whether or not the window exclusively captures the mouse.
self.exclusive = False
# When flying gravity has no effect and speed is increased.
self.flying = False
# Strafing is moving lateral to the direction you are facing,
# e.g. moving to the left or right while continuing to face forward.
#
# First element is -1 when moving forward, 1 when moving back, and 0
# otherwise. The second element is -1 when moving left, 1 when moving
# right, and 0 otherwise.
self.strafe = [0, 0]
# Current (x, y, z) position in the world, specified with floats. Note
# that, perhaps unlike in math class, the y-axis is the vertical axis.
self.position = (0, 0, 0)
# First element is rotation of the player in the x-z plane (ground
# plane) measured from the z-axis down. The second is the rotation
# angle from the ground plane up. Rotation is in degrees.
#
# The vertical plane rotation ranges from -90 (looking straight down) to
# 90 (looking straight up). The horizontal rotation range is unbounded.
self.rotation = (0, 0)
# Which sector the player is currently in.
self.sector = None
# The crosshairs at the center of the screen.
self.reticle = None
# Velocity in the y (upward) direction.
self.dy = 0
# A list of blocks the player can place. Hit num keys to cycle.
self.inventory = [BRICK, GRASS, SAND]
# The current block the user can place. Hit num keys to cycle.
self.block = self.inventory[0]
# Convenience list of num keys.
self.num_keys = [
key._1, key._2, key._3, key._4, key._5,
key._6, key._7, key._8, key._9, key._0]
# Instance of the model that handles the world.
self.model = Model()
# The label that is displayed in the top left of the canvas.
self.label = pyglet.text.Label('', font_name='Arial', font_size=18,
x=10, y=self.height - 10, anchor_x='left', anchor_y='top',
color=(0, 0, 0, 255))
# This call schedules the `update()` method to be called
# TICKS_PER_SEC. This is the main game event loop.
pyglet.clock.schedule_interval(self.update, 1.0 / TICKS_PER_SEC)
Here's the input handlers:
def on_mouse_press(self, x, y, button, modifiers):
""" Called when a mouse button is pressed. See pyglet docs for button
amd modifier mappings.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
button : int
Number representing mouse button that was clicked. 1 = left button,
4 = right button.
modifiers : int
Number representing any modifying keys that were pressed when the
mouse button was clicked.
"""
if self.exclusive:
vector = self.get_sight_vector()
block, previous = self.model.hit_test(self.position, vector)
if (button == mouse.RIGHT) or \
((button == mouse.LEFT) and (modifiers & key.MOD_CTRL)):
# ON OSX, control + left click = right click.
if previous:
self.model.add_block(previous, self.block)
if button == pyglet.window.mouse.LEFT and block:
texture = self.model.world[block]
self.model.remove_block(block)
else:
self.set_exclusive_mouse(True)
def on_mouse_motion(self, x, y, dx, dy):
""" Called when the player moves the mouse.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
dx, dy : float
The movement of the mouse.
"""
if self.exclusive:
m = 0.15
x, y = self.rotation
x, y = x + dx * m, y + dy * m
y = max(-90, min(90, y))
self.rotation = (x, y)
def on_key_press(self, symbol, modifiers):
if symbol == key.W:
self.strafe[0] -= 1
if symbol == key.S:
self.strafe[0] += 1
if symbol == key.A:
self.strafe[1] -= 1
if symbol == key.D:
self.strafe[1] += 1
if symbol == key.SPACE:
if self.dy == 0:
self.dy = JUMP_SPEED
if symbol == key.ESCAPE:
self.set_exclusive_mouse(False)
if symbol == key.TAB:
self.flying = not self.flying
if symbol in self.num_keys:
index = (symbol - self.num_keys[0]) % len(self.inventory)
self.block = self.inventory[index]
And finally, here's the setup:
""" Configure the OpenGL fog properties.
"""
# Enable fog. Fog "blends a fog color with each rasterized pixel fragment's
# post-texturing color."
glEnable(GL_FOG)
# Set the fog color.
glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1))
# Say we have no preference between rendering speed and quality.
glHint(GL_FOG_HINT, GL_DONT_CARE)
# Specify the equation used to compute the blending factor.
glFogi(GL_FOG_MODE, GL_LINEAR)
# How close and far away fog starts and ends. The closer the start and end,
# the denser the fog in the fog range.
glFogf(GL_FOG_START, 50.0)
glFogf(GL_FOG_END, 100.0)
def setup():
""" Basic OpenGL configuration.
"""
# Set the color of "clear", i.e. the sky, in rgba.
glClearColor(0.5, 0.69, 1.0, 1)
# Enable culling (not rendering) of back-facing facets -- facets that aren't
# visible to you.
glEnable(GL_CULL_FACE)
# Set the texture minification/magnification function to GL_NEAREST (nearest
# in Manhattan distance) to the specified texture coordinates. GL_NEAREST
# "is generally faster than GL_LINEAR, but it can produce textured images
# with sharper edges because the transition between texture elements is not
# as smooth."
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
setup_fog()
def main():
window = Window(width=800, height=600, caption='Minecraft', resizable=True)
# Hide the mouse cursor and prevent the mouse from leaving the window.
setup()
if __name__ == '__main__':
main()
pyglet.app.run()
Here's texture.png:
Here's an example of what's happening (the black circles means I've clicked the mouse, and at the end, I was rapidly pressing W):
gif
What I've Done
Here is what I've done so far:
Looked at the Pyglet Docs
Researched on google with various phrasing and keywords
It sounds like it could be a Mac related issue. Here is a bug report on something similar happening with Mac: https://github.com/pyglet/pyglet/issues/225
One thing I would try is just try a barebones setup and see if the problems persists with the minimal code. If this still occurs, there is most likely a bug in the Mac/Pyglet interaction. If the basic sample works, there might be a bug in the Minecraft example.
import pyglet
window = pyglet.window.Window()
#window.event
def on_draw():
print('on_draw')
window.clear()
#window.event
def on_mouse_motion(x, y, dx, dy):
print('on_mouse_motion', x, y, dx, dy)
#window.event
def on_key_press(symbol, modifiers):
print('on_key_press', symbol, modifiers)
pyglet.app.run()
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))
I'm making a little platformer game using pygame, and decided that making a level editor for each level would be easier than typing each blocks' coordinate and size.
I'm using a set of lines, horizontally and vertically to make a grid to make plotting points easier.
Here's the code for my grid:
def makeGrid(surface, width, height, spacing):
for x in range(0, width, spacing):
pygame.draw.line(surface, BLACK, (x,0), (x, height))
for y in range(0, height, spacing):
pygame.draw.line(surface, BLACK, (0,y), (width, y))
I want the user's mouse to move at 10px intervals, to move to only the points of intersection. Here's what I tried to force the mouse to snap to the grid.
def snapToGrid(mousePos):
if 0 < mousePos[0] < DISPLAYWIDTH and 0 < mousePos[1] < 700:
pygame.mouse.set_pos(roundCoords(mousePos[0],mousePos[1]))
(BTW, roundCoords() returns the coordinates rounded to the nearest ten unit.)
(Also BTW, snapToGrid() is called inside the main game loop (while not done))
...but this happens, the mouse doesn't want to move anywhere else.
Any suggestions on how to fix this? If I need to, I can change the grid code too.
Thanks a bunch.
P.S. This is using the latest version of PyGame on 64 bit Python 2.7
First of all I think you're not far off.
I think the problem is that the code runs quite fast through each game loop, so your mouse doesn't have time to move far before being set to the position return by your function.
What I would have a look into is rather than to pygame.mouse.set_pos() just return the snapped coordinates to a variable and use this to blit a marker to the screen highlighting the intersection of interest (here I use a circle, but you could just blit the image of a mouse ;) ). And hide your actual mouse using pygame.mouse.set_visible(False):
def snapToGrid(mousePos):
if 0 < mousePos[0] < DISPLAYWIDTH and 0 < mousePos[1] < 700:
return roundCoords(mousePos[0],mousePos[1])
snap_coord = snapToGrid(mousePos)# save snapped coordinates to variable
pygame.draw.circle(Surface, color, snap_coord, radius, 0)# define the remaining arguments, Surface, color, radius as you need
pygame.mouse.set_visible(False)# hide the actual mouse pointer
I hope that works for you !
I create a widget that plots data and allows users a selections:
https://gobblin.se/u/kellogs/m/bildschirmfoto-vom-2013-11-12-13-23-54/
Sadly, in this screenshot should appear multiple selections (here red), but rendering n of this models doesn't work for some reason. I made sure, that the data is available and the rendering is called and works fine (right position and dimensions)
So my widget just creates a cairo surface that is used in a iteration of the following method to render the selections on top of the plotted data line:
def render_to(self, cairosurface):
'''external trigger to redraw widget on channel widget surface (only within plotarea!)'''
cr = cairosurface
pos_x=self.__getXPos()
w = self.__getWidth()
h = self.__getHeight()
eogclass=self.eogclassification.eogclass
#background
r,g,b=eogclass.color
alpha=0.9
color=(r,g,b,alpha)
cr.set_source_rgba(*color)
cr.rectangle(pos_x, 0, w, h)
cr.fill()
#label
label=eogclass.name
cr.set_source_rgb(*eogclass.text_color)
cr.set_font_size(13)
(x, y, width, height, dx, dy) = cr.text_extents(label)
cr.move_to(pos_x+w/2 - width/2, h/2) #center within our block
cr.text_path(label)
cr.clip()
cr.stroke()
cr.paint()
Can anybody give me a tip what might be the problem?
I'm not sure, but can this be a problem with compositing?
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.