Access violation from drawing

40 views
Skip to first unread message

Neil

unread,
Apr 10, 2009, 3:13:25 AM4/10/09
to pyglet-users
Hello all,

I've put together a little flight simulator in pyglet. You can see it
here:
http://forums.tigsource.com/index.php?topic=5009.60
It runs fine on the two computers that I have access to, but at least
one person is getting this error:

...
File "C:\flight\project\buildflight\out1.pyz/view.hud", line 175, in
draw
File "...out1.pyz/pyglet.graphics", line 470, in <lambda>
File "...out1.pyz/pyglet.graphics.vertexdomain", line 286, in draw
WindowsError: exception: access violation writing 0x00000008

The "out1.pyz" is because I packaged it with pyinstaller. His system
is: Pentium Core 2 Quad 2.33GHz, Windows XP Professional 32bits SP3,
(almost) 4 GB RAM Dual-Channel, ATI Radeon HD4850 512 MB, 8.12 driver
version. There's a discussion of it at the end of the thread I linked
to.

The line in view.hud that it doesn't like is "batch.draw()", which
draws some cockpit instruments and readouts. Drawing the batch is
supposed to go like this:
1. Draw the background group (sprites for the faces of the instrument
dials)
2. Change line width to 5, and draw the thick_lines group (some
readouts and a hand on a dial)
3. Change line width to 3, and draw the thin_lines group (hands on
dials)
4. Draw the foreground group (some triangular pointers and some text)
If I comment out everything from the hud except for the bits that draw
the text, the game runs. An earlier version of the problem, before I
had organized the batch in this way, crashed the game at a point where
I was drawing a single thick line in the hud using
pyglet.graphics.draw.

I will post the whole of hud.py below. Unfortunately it has been
edited since I got the bug report above, but I don't think I did
anything other than comment and then uncomment some of it, to produce
the version with no hud.

Thanks for any help,

Neil

from __future__ import division
import pyglet
from math import *
from euclid import *
from pyglet.gl import *
from utilities import *

DIAL_OPACITY = 0.3
HAND_OPACITY = 0.6
TRACK_OPACITY = 0.6

batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
thick_lines = pyglet.graphics.OrderedGroup(1)
thin_lines = pyglet.graphics.OrderedGroup(2)
foreground = pyglet.graphics.OrderedGroup(3)

def set_thin_lines():
glLineWidth(GLfloat(3))
def set_thick_lines():
glLineWidth(GLfloat(5))

thick_lines.set_state = set_thick_lines
thin_lines.set_state = set_thin_lines


class Hud:
def __init__(self, plane, wingman, window, controller, \
instruments_x, instruments_y):
self.plane = plane
self.wingman = wingman
self.window = window
self.controller = controller
self.width = window.width
self.height = window.height
self.target_readout = pyglet.text.Label('',
font_name='Arial', bold = True,
font_size= 15,
color = (0,0,0,130),
multiline = True,
width = self.width, #necessary for multiline
x=30, y=85, batch = batch, group =
foreground)
self.wingman_readout = pyglet.text.Label('',
font_name='Arial', bold = True,
font_size= 15,
color = (0,0,0,130),
multiline = True,
width = self.width, #necessary for multiline
x=550, y=85, batch = batch, group =
foreground)
self.radio_readout = pyglet.text.Label('...',
font_name='Arial', bold = True,
font_size= 15,
color = (0,0,0,65),
multiline = True,
width = self.width, #necessary for multiline
x=30, y=45, batch = batch, group =
foreground)
self.artificial_horizon = \
ArtificialHorizon(self.plane, instruments_x,
instruments_y)
self.yoke_display = \
YokeDisplay(instruments_x+77, instruments_y)
self.aileron_display = \
AileronDisplay(instruments_x, instruments_y - 82)
self.throttle_display = \
ThrottleDisplay(instruments_x-77, instruments_y)
self.speed_dial = Dial(instruments_x-160, instruments_y, \
speed_dial, 6, 55, 3)
self.altitude_dial = Dial(instruments_x+160, instruments_y, \
altitude_dial, 6, 55, 3, 6, 30, 4)
#self.fps_display = pyglet.clock.ClockDisplay(color =
(1.0,1.0,1.0,0.5))

def update(self):
self.update_target_readout()
self.update_wingman_readout()
self.update_speed()
self.update_altitude()
self.update_yoke()
self.update_aileron()
self.update_throttle()

def update_target_readout(self):
target = self.controller.target
if target == None:
self.target_readout.text = "Target: None"
return
x, y, z = self.plane.body.vectorToWorld((-1,0,0))
bearing = (x, 0, z)
bearing = scale(bearing, 1/length(bearing))
left = cross(bearing, (0,1,0))
to_target = subtract(target.body.getPosition(),
self.plane.body.getPosition())
distance_to_target = length(to_target)
distance_in_front = dot(to_target, bearing)
distance_to_left = dot(to_target, left)
angle = 2*pi + atan2(distance_to_left, distance_in_front)
angle = int(round(12*angle/(2*pi)))
if angle>12:
angle -= 12
text = "Target: " + target.name + ", "
text += str(angle) +" o'clock, "
if to_target[1] > 200:
text += "high, "
elif to_target[1] < -200:
text += "low, "
else:
text += "level, "
text += str(100*int(distance_to_target/100))+ " yards"
# if target.type == 'alien':
# text+=" "+target.ai.status+" "+target.ai.target.name
# if target.ai.aggressive:
# text += " aggressive"
self.target_readout.text = text

def update_wingman_readout(self):
text = "Green 2 status: "+self.wingman.ai.status_string
self.wingman_readout.text = text

def update_radio_readout(self, string):
self.radio_readout.text = "Radio from Green 2: "+string
self.radio_readout.color = (0, 0, 0, 130)
pyglet.clock.schedule_once(self.fade_radio, 5) # 5 seconds

def fade_radio(self, dt):
self.radio_readout.color = (0, 0, 0, 65)

def update_speed(self):
knots = self.plane.get_forward_velocity()*1.94384449
#self.readout.text = "Airspeed: %.2f kts" % knots
# Dial starts from bottom at 50 knots and shows one knot per
degree
if knots<50:
rotation = 270
else:
rotation = 270 - (knots - 50)
self.speed_dial.set_rotation(radians(rotation))

def update_altitude(self):
feet = self.plane.get_position()[1]*3.2808399
# Dial starts from top, big hand rotates 360 degrees in 1000
feet,
# little hand rotates 360 degrees in 10000 feet.
big_rotation = 90 - feet * 360 / 1000
little_rotation = 90 - feet * 360 /10000
self.altitude_dial.set_rotation(radians(big_rotation), \
radians(little_rotation))

def update_yoke(self):
self.yoke_display.set_displacement
(self.controller.yoke.vertical)

def update_aileron(self):
self.aileron_display.set_displacement
(self.controller.yoke.horizontal)

def update_throttle(self):
self.throttle_display.set_setting
(self.controller.throttle.setting)
self.throttle_display.current_power = self.plane.power
self.throttle_display.engine_health = self.plane.engine_health

def setup_for_drawing(self):
glViewport(0, 0, self.width, self.height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0, self.width, 0, self.height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glDisable(GL_SCISSOR_TEST)
#glDisable(GL_LIGHTING)
#glClear(GL_DEPTH_BUFFER_BIT)

def draw(self):
self.yoke_display.update_draw()
self.aileron_display.update_draw()
self.throttle_display.update_draw()
self.speed_dial.update_draw()
self.altitude_dial.update_draw()
batch.draw()
self.artificial_horizon.draw()

class YokeDisplay:
def __init__(self, x, y):
self.x = x
self.y = y
self.displacement = 0
self.track_sprite = pyglet.sprite.Sprite(elevator_track,
batch = batch, group = background)
self.track_sprite.opacity = int(255*TRACK_OPACITY)
self.track_sprite.x = x
self.track_sprite.y = y
self.line = batch.add(4, GL_LINES, thick_lines, 'v2f', 'c4f')
self.pointer = batch.add(6, GL_TRIANGLES, foreground, 'v2f',
'c4f')
self.pointer.colors = (0.0, 0.0, 0.0, HAND_OPACITY)*6

def set_displacement(self,d):
self.displacement = d

def update_draw(self):
x, y, d = self.x, self.y, self.displacement
redness = clamp(abs(d)-25, 0, 25)/25
self.line.colors = (1.0, 1.0-redness, 1.0-redness,
HAND_OPACITY + 0.5*redness)*4
#glLineWidth(GLfloat(6))
self.line.vertices = (x-1, y, x-1, y+d,
x+1, y, x+1, y+d) #for thickness
self.pointer.vertices = (x+6, y+d, #elevator
x-6, y+d+3,
x-6, y+d-3,
x-6, y+d,
x+6, y+d-3,
x+6, y+d+3)

class AileronDisplay:
def __init__(self, x, y):
self.x = x
self.y = y
self.displacement = 0
self.track_sprite = pyglet.sprite.Sprite(aileron_track,
batch = batch, group = background)
self.track_sprite.opacity = int(255*TRACK_OPACITY)
self.track_sprite.x = x
self.track_sprite.y = y
self.pointer = batch.add(3, GL_TRIANGLES, foreground, 'v2f',
'c4f')
self.pointer.colors = (0.0, 0.0, 0.0, HAND_OPACITY)*3

def set_displacement(self,d):
self.displacement = d

def update_draw(self):
#glColor4f(0.0, 0.0, 0.0, HAND_OPACITY)
#glLineWidth(GLfloat(3))
x, y = self.x+self.displacement, self.y
self.pointer.vertices = (x, y, #aileron
x-4, y-8,
x+4, y-8)


class ThrottleDisplay:
def __init__(self, x, y):
self.x = x
self.y = y
self.setting = 0
self.current_power = 10
self.engine_health = 1
self.track_sprite = pyglet.sprite.Sprite(throttle_track,
batch = batch, group = background)
self.track_sprite.opacity = int(255*TRACK_OPACITY)
self.track_sprite.x = x
self.track_sprite.y = y
self.line = batch.add(2, GL_LINES, thick_lines, 'v2f', 'c4f')
self.line.colors = (1.0, 1.0, 1.0, HAND_OPACITY)*2
self.pointer = batch.add(3, GL_TRIANGLES, foreground, 'v2f',
'c4f')
self.pointer.colors = (0.0, 0.0, 0.0, HAND_OPACITY)*3

def set_setting(self,d):
self.setting = d

def set_power(self, p):
self.current_power = p

def update_draw(self):
self.whiteness = clamp(self.engine_health*10-9, 0, 1)
self.line.colors = (1.0, self.whiteness, self.whiteness,
HAND_OPACITY)*2
x, y = self.x, self.y - 50 + self.setting
self.pointer.vertices = (x-1, y, # throttle setting
x-6, y+3,
x-6, y-3)
self.line.vertices = (self.x+5, self.y-51, # engine power
self.x+5, self.y-50 + self.current_power)

class Dial:
def __init__(self, x, y, dial_image,
big_hand_start, big_hand_end, big_hand_thickness, \
little_hand_start = 0, little_hand_end = 0, \
little_hand_thickness = 0):
self.x = x
self.y = y
self.dial_sprite = pyglet.sprite.Sprite(dial_image,
batch = batch, group = background)
self.dial_sprite.opacity = int(255*DIAL_OPACITY)
self.dial_sprite.x = x
self.dial_sprite.y = y
self.big_hand_start = big_hand_start
self.big_hand_end = big_hand_end
self.big_hand_thickness = big_hand_thickness
self.big_rotation = 0
self.big_line = batch.add(2, GL_LINES, thin_lines, 'v2f',
'c4f')
self.big_line.colors = (1.0,1.0,1.0,HAND_OPACITY)*2
self.little_hand_start = little_hand_start
self.little_hand_end = little_hand_end
self.little_hand_thickness = little_hand_thickness
self.little_rotation = 0
if little_hand_thickness > 0:
self.has_little_hand = True
self.little_line = batch.add(2, GL_LINES, thick_lines,
'v2f', 'c4f')
self.little_line.colors = (1.0,1.0,1.0,HAND_OPACITY)*2
else:
self.has_little_hand = False

def set_rotation(self, big_rotation, little_rotation = 0):
self.big_rotation = big_rotation
self.little_rotation = little_rotation

def update_draw(self):
if self.has_little_hand:
self.draw_hand(self.little_line, self.little_hand_start,
self.little_hand_end, \
self.little_rotation)
self.draw_hand(self.big_line, self.big_hand_start,
self.big_hand_end, \
self.big_rotation)

def draw_hand(self, line, start, end, rotation):
rx = cos(rotation)
ry = sin(rotation)
line.vertices = (self.x + rx*start, self.y + ry*start, \
self.x + rx*end, self.y + ry*end)

class ArtificialHorizon:
def __init__(self,plane,x,y):
self.plane = plane
self.x = x
self.y = y
self.background = pyglet.sprite.Sprite(horizon,
batch = batch, group = background)
self.background.opacity = int(255*DIAL_OPACITY)
self.background.x = x
self.background.y = y
self.radius = 47
self.boss_radius = 8
self.mark = pyglet.graphics.vertex_list(2,
('v2f', (0, -self.radius, 0, -self.radius*0.8)),
('c4f', (1.0, 1.0, 1.0, HAND_OPACITY)*2))
self.horizon = pyglet.graphics.vertex_list(4, 'v2f',
('c4f', (1.0, 1.0, 1.0, HAND_OPACITY)*4))


def draw(self):
sin_pitch = self.plane.get_sin_pitch()
bank_angle = self.plane.get_bank_angle()
glPushMatrix()
glTranslatef(self.x, self.y, 0)
glRotatef(degrees(bank_angle), 0, 0, -1)
# draw mark in direction of ground
glLineWidth(GLfloat(4))
self.mark.draw(GL_LINES)
scaled_pitch = sin_pitch * 1.1
# will only display lines if scaled_pitch<1, so the above line
# stops us showing pitches very close to 1.
glLineWidth(GLfloat(3))
if abs(scaled_pitch)<1:
horizon_width = self.radius*sqrt(1-scaled_pitch**2)
horizon_height = -self.radius*scaled_pitch
if horizon_width > self.boss_radius:
self.horizon.vertices[:]= \
(self.boss_radius, horizon_height,
horizon_width, horizon_height,
-self.boss_radius, horizon_height,
-horizon_width, horizon_height)
self.horizon.draw(GL_LINES)
glPopMatrix()
#self.foreground.draw()

speed_dial = pyglet.image.load('graphics/dial.png')
speed_dial.anchor_x = speed_dial.width // 2
speed_dial.anchor_y = speed_dial.height // 2
horizon = pyglet.image.load('graphics/horizon.png')
horizon.anchor_x = horizon.width // 2
horizon.anchor_y = horizon.height //2 +1
altitude_dial = pyglet.image.load('graphics/alt_dial.png')
altitude_dial.anchor_x = altitude_dial.width // 2
altitude_dial.anchor_y = altitude_dial.height // 2
elevator_track = pyglet.image.load('graphics/elevator_track.png')
elevator_track.anchor_x = elevator_track.width // 2
elevator_track.anchor_y = elevator_track.height // 2
throttle_track = pyglet.image.load('graphics/throttle_track.png')
throttle_track.anchor_x = throttle_track.width // 2
throttle_track.anchor_y = throttle_track.height // 2
aileron_track = pyglet.image.load('graphics/aileron_track.png')
aileron_track.anchor_x = aileron_track.width // 2
aileron_track.anchor_y = aileron_track.height // 2

Neil

unread,
Apr 14, 2009, 3:00:42 AM4/14/09
to pyglet-users
Sorry if I wasn't clear what I was asking in the previous post. I
can't reproduce this problem myself, so it's tough to debug, and I'm
wondering if there's something obvious I'm doing wrong which people
would expect to give problems with portability. I'm a newcomer to
pyglet and opengl. I've revised the code a bit from what I posted
above; the latest bug report someone sent is just "File "C:\flight
\project\buildflight\out1.pyz/pyglet.graphics.vertexdomain", line 278,
in draw; WindowsError: exception: access violation reading 0x00000000"

Things that I think might be problems:
1. I guess this is crazy, but the bug was reported at a spot in pyglet
where it was drawing a vertex list for me that consisted of a single
primitive. I've added dummy primitives to these vertex lists now, and
it hasn't gone away (I don't know if it has moved).
2. A reason I thought 1. is possible is the issue mentioned in the
manual about line strips and triangle fans; but I'm not using those.
3. Could I be using batches and groups wrong? I defined some groups in
the HUD code, but used "none" everywhere else. However in a profiler,
I'm seeing calls made to the "set state" function of one of the HUD
groups, when drawing something that isn't part of the HUD.
4. For some things (not the HUD) I'm using the nverts2.py code posted
by tamas in this newsgroup (thanks!). Maybe this could be a problem?
It provides a custom batch class, which you can use to make vertex
lists that can be used as arrays in numpy.
5. Perhaps I'm not being careful enough about what works in different
versions of opengl. I use a lot of gl_lines with large widths. These
work fine on the 2004 graphics card on my laptop, but maybe this is
something being phased out on newer cards, like at least one of the
guys with the problem has? Are there some more checks that I should be
doing for opengl compatibility? (Currently I've got one for
gl_point_parameters, which I used elsewhere and found that one of the
computers at work couldn't handle.)

One other thing that I noticed, which may be relevant: the biggest
optimization I have made was to replace the instrument panel, which is
drawn in the above code by a series of sprites, by a single blitted
image. The profiler showed the sprites taking a very long time to set
up and draw. Replacing them with a single sprite didn't change this
much, but using blit rather than drawing a sprite increased my
framerate from around 30 fps to around 50.

Thanks for any advice.
Reply all
Reply to author
Forward
0 new messages