class Applet(pyglet.window.Window): def update(self, dt): cam = self.cam if self.exclusive: if key.A in self.keys: cam.roll += 4 if key.S in self.keys: cam.roll -= 4 x0, y0, z0 = cam.x, cam.y, cam.z cam.move(0, 0, -self.speed * 128 * 0.003) x1, y1, z1 = cam.x, cam.y, cam.z iz0, iz1 = int(z0), int(z1) # Now checking if you passed a torus if iz0 // 10 != iz1 // 10: dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 for i in xrange(iz1 - iz0): # Passed a torus boundary torus = iz0 // 10 + i + 1 if torus >= len(self.world.tori): break tz = torus * 10 tx, ty = self.world.tori[torus] factor = (tz - z0) / dz ax, ay = factor * dx + x0, factor * dy + y0 distance = hypot(ax - tx, ay - ty) if distance > TORUS_MAJOR: # Oops, you just got out of a torus! self.score -= 10 if OVERLAY: def hit(err): w, h = self.get_size() glPushAttrib(GL_CURRENT_BIT) glEnable(GL_BLEND) glColor4f(1, 0, 0, err / 100.0) glBegin(GL_QUADS) glVertex2f(0, 0) glVertex2f(0, h) glVertex2f(w, h) glVertex2f(w, 0) glEnd() glPopAttrib() return err - 1 if not self.debug: self.overlays[hit] = 50 else: self.score += 1 self.progress += 1 self.speed += DELTA for entity in self.world.tracker: entity.update() def __init__(self, *args, **kwargs): super(Applet, self).__init__(*args, **kwargs) l = clock() self.fps = 0 self.world = load_world("world.json") self.speed = INITIAL_SPEED self.score = 20 self.progress = 0 self.keys = [] self.overlays = {} self.info = True self.debug = False self.label = pyglet.text.Label('', font_name='Consolas', font_size=12, x=10, y=self.height - 20, color=(255,) * 4, multiline=True, width=600) # Resize is called on startup anyways, so we can start with 0 values. self.cam = Camera() self.exclusive = False pyglet.clock.schedule_interval(self.update, 1.0 / TICKS_PER_SECOND) def update_fps(dt): self.fps = pyglet.clock.get_fps() pyglet.clock.schedule_interval(update_fps, 1) glClearColor(0, 0, 0, 1) glClearDepth(1.0) texture.init() if not texture.badcard: print 'Not bad card' glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glEnable(GL_LINE_SMOOTH) glEnable(GL_POLYGON_SMOOTH) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) glAlphaFunc(GL_GEQUAL, 0.9) glDepthFunc(GL_LEQUAL) glEnable(GL_DEPTH_TEST) glShadeModel(GL_SMOOTH) glMatrixMode(GL_MODELVIEW) glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glEnable(GL_LIGHT1) glEnable(GL_POLYGON_OFFSET_FILL) fv4 = GLfloat * 4 glLightfv(GL_LIGHT0, GL_POSITION, fv4(.5, .5, 1, 0)) glLightfv(GL_LIGHT0, GL_SPECULAR, fv4(.5, .5, 1, 1)) glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4(1, 1, 1, 1)) glLightfv(GL_LIGHT1, GL_POSITION, fv4(1, 0, .5, 0)) glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1)) glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1)) self.torus_id = compile(torus, 3.0, 0.25, TORUS_DETAIL ** 2, TORUS_DETAIL ** 2, (.07, .37, 1, 2)) self.cl_torus_id = compile(torus, 3.0, 0.25, TORUS_DETAIL ** 2, TORUS_DETAIL ** 2, (0, 1, 0, 1)) self.asteroid_ids = [model_list(load_model(r"asteroids\01.obj"), 5, 5, 5, (0, 0, 0)), model_list(load_model(r"asteroids\02.obj"), 5, 5, 5, (0, 0, 0)), model_list(load_model(r"asteroids\03.obj"), 5, 5, 5, (0, 0, 0)), model_list(load_model(r"asteroids\04.obj"), 5, 5, 5, (0, 0, 0)), model_list(load_model(r"asteroids\05.obj"), 5, 5, 5, (0, 0, 0))] c = self.cam # Position camera at first torus... (c.x, c.y), c.z = self.world.tori[0], 0 c.z -= 10 # ... well, 10 units away... self.cam.yaw = 180 # ... and face the torus. print "Loaded in %s seconds." % (clock() - l) def set_exclusive_mouse(self, exclusive): super(Applet, self).set_exclusive_mouse(exclusive) # Pass to parent self.exclusive = exclusive # Toggle flag def on_mouse_press(self, x, y, button, modifiers): if not self.exclusive: self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): if self.exclusive: # Only handle camera movement if mouse is grabbed self.cam.mouse_move(dx * MOUSE_SENSITIVITY, dy * MOUSE_SENSITIVITY) def on_key_press(self, symbol, modifiers): if self.exclusive: # Only handle keyboard input if mouse is grabbed if symbol == key.ESCAPE: pyglet.app.exit() elif symbol == key.E: self.set_exclusive_mouse(False) # Escape mouse elif symbol == key.F: self.set_fullscreen(not self.fullscreen) elif symbol == key.NUM_ADD: self.speed += 1 elif symbol == key.NUM_SUBTRACT: self.speed -= 1 elif symbol == key.NUM_MULTIPLY: self.speed += 10 elif symbol == key.NUM_DIVIDE: self.speed -= 10 elif symbol == key.PAGEUP: self.speed += 100 elif symbol == key.PAGEDOWN: self.speed -= 100 elif symbol == key.I: self.info = not self.info elif symbol == key.D: self.debug = not self.debug elif symbol == key.Q: c = self.cam dx, dy, dz = c.direction() speed = max(1, abs(self.speed) * 0.6) dx *= speed dy *= speed dz *= speed self.world.tracker.append( Asteroid(random.choice(self.asteroid_ids), (c.x, c.y - 3, c.z + 5), direction=(dx, dy, dz))) else: self.keys.append(symbol) def on_key_release(self, symbol, modifiers): if symbol in self.keys: self.keys.remove(symbol) def on_resize(self, width, height): height = max(height, 1) # Prevent / by 0 self.label.y = height - 20 glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() # A field of view of 45 gluPerspective(45.0, width / float(height), 0.1, 50000000.0) glMatrixMode(GL_MODELVIEW) def on_draw(self): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() c = self.cam x, y, z = c.x, c.y, c.z glRotatef(c.pitch, 1, 0, 0) glRotatef(c.yaw, 0, 1, 0) glRotatef(c.roll, 0, 0, 1) glTranslatef(-x, -y, -z) if self.world.waypoints and self.debug: glDisable(GL_LIGHTING) glPushAttrib(GL_LINE_BIT | GL_CURRENT_BIT) glColor3f(0, 1, 0) glLineWidth(3) glBegin(GL_LINE_STRIP) for waypoint in self.world.waypoints: glVertex3f(*waypoint) glEnd() glPopAttrib() glEnable(GL_LIGHTING) glEnable(GL_BLEND) world = self.world if x != world.x or y != world.y or z != world.z: world.tracker.sort(key=entity_distance(x, y, z), reverse=True) world.tracker.sort(key=attrgetter('background'), reverse=True) world.x, world.y, world.z = x, y, z flipped = c.pitch > 90 or c.pitch < -90 zi = int(z) / 10 * 10 if (not flipped and 90 <= c.yaw < 270) or (flipped and (c.yaw < 90 or c.yaw >= 270)): zi += 10 range = xrange(max(0, zi - 10), min(zi + VIEW_DISTANCE * 10, len(world.tori) * 10), 10) else: zi += 10 range = xrange(max(0, zi - VIEW_DISTANCE * 10), min(zi + 10, len(world.tori) * 10), 10) for z in range: torus = z / 10 x, y = world.tori[torus] id = self.cl_torus_id if z == zi else self.torus_id glPushMatrix() glTranslatef(x, y, z) glPushAttrib(GL_TRANSFORM_BIT | GL_CURRENT_BIT) glCallList(id) glPopAttrib() glPopMatrix() for entity in world.tracker: x, y, z = entity.location pitch, yaw, roll = entity.rotation glPushMatrix() glTranslatef(x, y, z) glRotatef(pitch, 1, 0, 0) glRotatef(yaw, 0, 1, 0) glRotatef(roll, 0, 0, 1) glPushAttrib(GL_CURRENT_BIT) glCallList(entity.id) if self.debug: glPushMatrix() glLineWidth(0.25) glPolygonOffset(1, 1) glDisable(GL_LIGHTING) glDisable(GL_TEXTURE_2D) glColor3f(0, 1, 0) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glCallList(entity.id) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) glEnable(GL_LIGHTING) glEnable(GL_TEXTURE_2D) glPopMatrix() glPopAttrib() glPopMatrix() if hasattr(entity, 'atmosphere') and entity.atmosphere: glPushMatrix() x0, y0, z0 = entity.location dx, dy, dz = x - x0, y - y0, z - z0 distance = sqrt(dz * dz + dx * dx) pitch = (360 - degrees(atan2(dy, distance))) yaw = degrees(atan2(dx, dz)) glTranslatef(x0, y0, z0) glRotatef(pitch, 1, 0, 0) glRotatef(yaw, 0, 1, 0) glCallList(entity.atmosphere) glPopMatrix() glColor4f(1, 1, 1, 1) glDisable(GL_TEXTURE_2D) width, height = self.get_size() if self.info: ortho(width, height) for pointer, err in dict(self.overlays).iteritems(): err = pointer(err) if not err: self.overlays.pop(pointer, None) else: self.overlays[pointer] = err progress_bar(5, 5, 10, 2, min(self.progress / len(self.world.tori), 1) * 100, type=VERTICAL) self.label.text = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) # %s: %s points\n' 'Direction(pitch=%.2f, yaw=%.2f, roll=%.2f)') % ( self.fps, c.x, c.y, c.z, self.speed, self.score, c.pitch, c.yaw, c.roll) self.label.draw() glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT) glLineWidth(2) cx, cy = width / 2, height / 2 glColor3f(0, 0, 1) crosshair(15, (cx, cy)) glColor4f(0, 1, 0, 1) circle(20, 30, (cx, cy)) glPopAttrib() frustrum()
class PycraftApplet(pyglet.window.Window): def load_textures(self): print 'Loading textures...' # Simply loading the texture binds it to the current OpenGL context PycraftApplet.ATLAS_ID = load_texture('assets/terrain.png') def update(self, dt): for symbol in self.keys: if self.keys[symbol]: x, y, z = self.cam.x, self.cam.y, self.cam.z self.cam.keyboard(128, self.speed, self.speed, self.speed, symbol) dx, dy, dz = self.cam.x, self.cam.y, self.cam.z bdx, bdy, bdz = int(dx / 2), int(dy / 2), int(dz / 2) bx, by, bz = int(x / 2), int(y / 2), int(z / 2) _x, _y, _z = dx, dy, dz height = 2 for i in xrange(-height, height): if self.world.get_block((bdx, bdy - height, bdz)): _y = y break if self.world.get_block((x, _y, bdz + 1)): _z = z self.cam.x, self.cam.y, self.cam.z = _x, _y, _z chunk = (bdx / 8, bdz / 8) if chunk not in self.queued: self.world.generate_chunk(chunk, async=True) self.queued.append(chunk) def __init__(self, *args, **kwargs): super(PycraftApplet, self).__init__(*args, **kwargs) self.queued = [] self.world = World("overworld", OverworldChunkProvider(random.randint(1, 20))) # That seed. self.speed = 2 self.world_renderer = WorldRenderer(self.world) self.keys = {} self.label = pyglet.text.Label('', font_name='Consolas', font_size = 13, x = 10, y = self.height - 20, color = (0, 0, 0, 255)) # Resize is called on window start anyways, so we can start with 0 values. self.cam = Camera(0, 0, 60, 0, 0, 0, 0, 0, 0) self.exclusive = False TPS = 40 # Update 40 times a second pyglet.clock.schedule_interval(self.update, 1.0 / TPS) glEnable(GL_TEXTURE_2D) glClearColor(0.78, 0.86, 1.0, 1) glClearDepth(1.0) glDepthFunc(GL_LESS) glEnable(GL_DEPTH_TEST) glShadeModel(GL_SMOOTH) glEnable(GL_POLYGON_OFFSET_FILL) # Stops z-buffer fighting # Comment if PyCraft lags glEnable(GL_FOG) glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.78, 0.86, 1.0, 1)) glHint(GL_FOG_HINT, GL_NICEST) glFogi(GL_FOG_MODE, GL_LINEAR) # Specify how close fog should start to the camera glFogf(GL_FOG_START, 100.0) glFogf(GL_FOG_END, 200.0) self.cam.apply_perspective() glMatrixMode(GL_MODELVIEW) self.load_textures() def set_exclusive_mouse(self, exclusive): super(PycraftApplet, self).set_exclusive_mouse(exclusive) # Pass to parent self.exclusive = exclusive # Toggle flag def selected_block(self): '''Write-only code to determine which block the player is looking at. If this stops working, abandon all hope. And cry.''' c = self.cam (dx, dy, dz) = c.direction() # Multiply the direction vector by 0..64 and check if a block exists there for m in xrange(64): # pos is always in world units; / 2 to get array index (x, y, z) = int(dx * m + c.x), int(dy * m + c.y), int(dz * m + c.z) chunk_pos = (x / 8 / 2, z / 8 / 2) # / by 8 to get chunk, and / 2 to get it in world coords '''if self.world.get_block((pos[0] / 2 & ~1, pos[1] / 2 & ~1, pos[2] / 2 & ~1)): print "yes." return (pos[0] & ~1, pos[1] & ~1, pos[2] & ~1)''' if chunk_pos in self.world.chunks: # Chunk is generated and exists ch_x = x / 2 % 8 # the block chosen in a chunk is the block % 8, as each chunk is 8x8 ch_z = z / 2 % 8 if (ch_x, y / 2, ch_z) in self.world.chunks[chunk_pos].data: # Check if our chunk does exist return (ch_x * 2 + chunk_pos[0] * 8 * 2, y & ~1, ch_z * 2 + chunk_pos[1] * 8 * 2) return None # Player is not looking at a block, or is too far from a block def on_mouse_press(self, x, y, button, modifiers): if self.exclusive: block = self.selected_block() if block: chunk = (int(block[0] / 8 / 2), int(block[2] / 8 / 2)) self.world.chunks[chunk].data.pop((block[0] / 2 % 8, block[1] / 2, block[2] / 2 % 8), None) # Remove the block from the world self.world_renderer.mark_chunk_dirty(chunk) else: self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): if self.exclusive: # Only handle camera movement if mouse is grabbed self.cam.mouse_move(dx, dy) def on_key_press(self, symbol, modifiers): if self.exclusive: # Only handle keyboard input if mouse is grabbed if symbol == key.ESCAPE: sys.exit() # Quit game if symbol == key.E: self.set_exclusive_mouse(False) # Escape mouse elif symbol == key.F: self.set_fullscreen(not self.fullscreen) elif symbol == key.NUM_ADD: self.speed += 3 elif symbol == key.NUM_SUBTRACT: self.speed -= 3 else: self.keys[symbol] = True def on_key_release(self, symbol, modifiers): self.keys[symbol] = False def on_resize(self, width, height): height = max(height, 1) # Prevent / by 0 self.label.y = height - 20 glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() # A field of view of 45 gluPerspective(45.0, width / float(height), 0.1, 1000.0) glMatrixMode(GL_MODELVIEW) def on_draw(self): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() self.cam.translate() self.world_renderer.draw_chunks((int(self.cam.x / 8 / 2), int(self.cam.z / 8 / 2)), 3) # Draw focused block frame block = self.selected_block() if block: glPolygonOffset(1, 1) # Disable textures; we don't want our focus to be textured! glDisable(GL_TEXTURE_2D) glPushAttrib(GL_CURRENT_BIT) # Black cursor colour glColor3f(0, 0, 0) # Will have a thickness of 2 glLineWidth(2) # Start drawing in wireframe mode glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) '''Draw with a quad. The current polygon mode will only draw the edges, so if we were to use triangles, then there would be an edge cutting through the the selected block's faces.''' glBegin(GL_QUADS) # Trick tesselator into drawing without texture by passing null block draw_face(None, block[0], block[1], block[2], ALL) glEnd() # Exit wireframe; start filling glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) glPopAttrib() glEnable(GL_TEXTURE_2D) width, height = self.get_size() glDisable(GL_DEPTH_TEST) glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() glOrtho(0, width, 0, height, -1, 1) glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity(); x, y, z = self.cam.x, self.cam.y, self.cam.z self.label.text = '%d FPS\n@ (x=%.2f, y=%.2f, z=%.2f, cx=%s, cy=%s)\n%s chunks loaded.' % (pyglet.clock.get_fps(), x, y, z, int(self.cam.x / 8 / 2), int(self.cam.z / 8 / 2), len(self.world.chunks)) self.label.draw() glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT) glColor3f(0, 0, 0) glLineWidth(3) glBegin(GL_LINES) CROSSHAIR_SIZE = 15 glVertex2f(width / 2 - CROSSHAIR_SIZE, height / 2) glVertex2f(width / 2 + CROSSHAIR_SIZE, height / 2) glVertex2f(width / 2, height / 2 - CROSSHAIR_SIZE) glVertex2f(width / 2, height / 2 + CROSSHAIR_SIZE) glEnd() glPopAttrib() glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glPopMatrix() glEnable(GL_DEPTH_TEST) glEnable(GL_TEXTURE_2D)