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