mirror of
https://github.com/bendtherules/pytry.git
synced 2026-08-18 22:03:03 +00:00
1303 lines
38 KiB
Python
1303 lines
38 KiB
Python
## Gloss
|
|
## Copyright (C) 2009 Paul Hudson (http://www.tuxradar.com/gloss)
|
|
|
|
## Gloss is free software; you can redistribute it and/or
|
|
## modify it under the terms of the GNU Lesser General Public License
|
|
## as published by the Free Software Foundation; either version 3
|
|
## of the License, or (at your option) any later version.
|
|
|
|
## This program is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
|
|
## You should have received a copy of the GNU Lesser General Public License
|
|
## along with this program; if not, write to the Free Software
|
|
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
from __future__ import division
|
|
|
|
import math
|
|
import os
|
|
import pygame
|
|
import random
|
|
import signal
|
|
import sys
|
|
import threading
|
|
import time
|
|
import traceback
|
|
import weakref
|
|
|
|
from OpenGL.GL import *
|
|
OpenGL.ERROR_CHECKING = False
|
|
from OpenGL.GL.EXT.framebuffer_object import *
|
|
from OpenGL.GLU import *
|
|
|
|
from pygame.locals import *
|
|
|
|
|
|
class GlossGame(object):
|
|
def __init__(self, name):
|
|
Gloss.active_game = self
|
|
|
|
Gloss.game_name = name
|
|
Gloss.screen_resolution = 1280,720
|
|
Gloss.full_screen = False
|
|
Gloss.enable_multisampling = False
|
|
|
|
# set empty event handlers
|
|
self.on_mouse_motion = None
|
|
self.on_mouse_down = None
|
|
self.on_mouse_up = None
|
|
self.on_key_down = None
|
|
self.on_key_up = None
|
|
self.on_quit = None
|
|
self.on_joy_axis_motion = None
|
|
self.on_joy_ball_motion = None
|
|
self.on_joy_button_down = None
|
|
self.on_joy_button_up = None
|
|
self.on_joy_hat_motion = None
|
|
|
|
def preload_content(self):
|
|
pass
|
|
|
|
def load_content(self):
|
|
pass
|
|
|
|
def draw_loading_screen(self):
|
|
pass
|
|
|
|
def draw(self):
|
|
pass
|
|
|
|
def update(self):
|
|
pass
|
|
|
|
def gloss_initialise(self):
|
|
os.environ['SDL_VIDEO_CENTERED'] = '1'
|
|
pygame.mixer.pre_init(44100, 16, 2, 4096)
|
|
pygame.init()
|
|
|
|
pygame.joystick.init()
|
|
for i in range(pygame.joystick.get_count()):
|
|
joystick = pygame.joystick.Joystick(i)
|
|
joystick.init()
|
|
Gloss.joysticks.append(joystick)
|
|
|
|
Gloss.game_clock = pygame.time.Clock()
|
|
|
|
pygame.display.set_caption(Gloss.game_name)
|
|
|
|
if Gloss.enable_multisampling:
|
|
pygame.display.gl_set_attribute(pygame.locals.GL_MULTISAMPLEBUFFERS, 1)
|
|
pygame.display.gl_set_attribute(pygame.locals.GL_MULTISAMPLESAMPLES, 4)
|
|
|
|
if Gloss.full_screen:
|
|
pygame.display.set_mode(Gloss.screen_resolution, pygame.OPENGL | pygame.DOUBLEBUF | pygame.FULLSCREEN)
|
|
else:
|
|
pygame.display.set_mode(Gloss.screen_resolution, pygame.OPENGL | pygame.DOUBLEBUF)
|
|
|
|
if Gloss.screen_resolution == (0, 0):
|
|
scr = pygame.display.get_surface()
|
|
Gloss.screen_resolution = scr.get_size()
|
|
|
|
Gloss.MaxTextureSize = glGetInteger(GL_MAX_TEXTURE_SIZE);
|
|
|
|
Gloss.clear(Color.CORNFLOWER_BLUE)
|
|
pygame.display.flip()
|
|
Gloss.clear(Color.CORNFLOWER_BLUE)
|
|
|
|
glEnable(GL_CULL_FACE)
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
|
|
|
|
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
|
|
glEnable(GL_LINE_SMOOTH)
|
|
|
|
glEnable(GL_TEXTURE_2D)
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
glEnable(GL_BLEND)
|
|
|
|
glEnable(GL_LIGHTING)
|
|
glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
|
|
glEnable (GL_COLOR_MATERIAL)
|
|
Gloss.set_scene_tint(Color.WHITE)
|
|
|
|
glMatrixMode(GL_PROJECTION)
|
|
glLoadIdentity()
|
|
glOrtho(0, Gloss.screen_resolution[0], Gloss.screen_resolution[1], 0, -100, 100)
|
|
|
|
Gloss.viewport_size = Gloss.screen_resolution
|
|
|
|
glMatrixMode(GL_MODELVIEW)
|
|
glLoadIdentity()
|
|
|
|
self.preload_content()
|
|
self.draw_loading_screen()
|
|
pygame.display.flip()
|
|
|
|
Gloss.tick_count = 0
|
|
|
|
self.load_content()
|
|
|
|
def gloss_internal_update(self):
|
|
for particlesystem in reversed(Gloss.auto_particle_systems):
|
|
# don't update this particle system if it's frozen
|
|
if particlesystem.frozen:
|
|
continue
|
|
|
|
if particlesystem.update() is False:
|
|
Gloss.auto_particle_systems.remove(particlesystem)
|
|
particlesystem.alive = False
|
|
|
|
if particlesystem.on_finish is not None:
|
|
particlesystem.on_finish(particlesystem)
|
|
|
|
def gloss_internal_draw(self):
|
|
# if we take a screenshot after picking, we get the picking view rather than the real view
|
|
# to fix this, save_screenshot() looks for Gloss.redraw_needed, and, if it's true, redraws the screen
|
|
if Gloss.picking:
|
|
Gloss.redraw_needed = True
|
|
else:
|
|
Gloss.redraw_needed = False
|
|
|
|
self.draw()
|
|
|
|
def run(self):
|
|
self.gloss_initialise()
|
|
|
|
Gloss.game_is_running = True
|
|
|
|
while Gloss.game_is_running:
|
|
Gloss.elapsed_seconds = Gloss.game_clock.tick(60) / 1000
|
|
Gloss.total_seconds += Gloss.elapsed_seconds
|
|
|
|
if Gloss.elapsed_seconds > 0.02:
|
|
Gloss.running_slowly = True
|
|
else:
|
|
Gloss.running_slowly = False
|
|
|
|
# tick_count is used in lots of places, so read it only once per update
|
|
Gloss.tick_count = pygame.time.get_ticks()
|
|
|
|
for event in pygame.event.get():
|
|
if event.type == MOUSEMOTION:
|
|
if self.on_mouse_down is not None:
|
|
self.on_mouse_motion(event)
|
|
|
|
if event.type == MOUSEBUTTONDOWN:
|
|
if self.on_mouse_down is not None:
|
|
self.on_mouse_down(event)
|
|
|
|
if event.type == MOUSEBUTTONUP:
|
|
if self.on_mouse_up is not None:
|
|
self.on_mouse_up(event)
|
|
|
|
if Gloss.sprite_click_tracking:
|
|
Gloss.select_object(event.pos)
|
|
|
|
if event.type == KEYDOWN:
|
|
if event.mod & KMOD_LCTRL == KMOD_LCTRL:
|
|
if event.key == K_ESCAPE:
|
|
# Left Ctrl+Escape held - quit
|
|
Gloss.game_is_running = False
|
|
|
|
if self.on_key_down is not None:
|
|
self.on_key_down(event)
|
|
|
|
if event.type == KEYUP:
|
|
if self.on_key_up is not None:
|
|
self.on_key_up(event)
|
|
|
|
if event.type == JOYAXISMOTION:
|
|
if self.on_joy_axis_motion is not None:
|
|
self.on_joy_axis_motion(event)
|
|
|
|
if event.type == JOYBALLMOTION:
|
|
if self.on_joy_ball_motion is not None:
|
|
self.on_joy_ball_motion(event)
|
|
|
|
if event.type == JOYHATMOTION:
|
|
if self.on_joy_hat_motion is not None:
|
|
self.on_joy_hat_motion(event)
|
|
|
|
if event.type == JOYBUTTONDOWN:
|
|
if self.on_joy_button_down is not None:
|
|
self.on_joy_button_down(event)
|
|
|
|
if event.type == JOYBUTTONUP:
|
|
if self.on_joy_button_up is not None:
|
|
self.on_joy_button_up(event)
|
|
|
|
if event.type == QUIT:
|
|
Gloss.game_is_running = False
|
|
|
|
# this all has to be done after the above events so that any changes from events
|
|
# take place as soon as possible
|
|
self.gloss_internal_update()
|
|
self.update()
|
|
|
|
self.gloss_internal_draw()
|
|
|
|
pygame.display.flip()
|
|
|
|
if self.on_quit is not None:
|
|
self.on_quit()
|
|
|
|
pygame.quit()
|
|
|
|
def quit(self):
|
|
Gloss.game_is_running = False
|
|
|
|
class Color(object):
|
|
def __init__(self, r, g, b, a = 1.0):
|
|
self.r = r
|
|
self.g = g
|
|
self.b = b
|
|
self.a = a
|
|
|
|
@staticmethod
|
|
def from_bytes(r, g, b, a = 255):
|
|
return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0)
|
|
|
|
@staticmethod
|
|
def from_html(col):
|
|
pygamecol = pygame.color.Color(col)
|
|
return Color.from_pygame(pygamecol)
|
|
|
|
@staticmethod
|
|
def from_pygame(col):
|
|
return Color(col[0] / 255, col[1] / 255, col[2] / 255, col[3] / 255)
|
|
|
|
@staticmethod
|
|
def lerp(colorfrom, colorto, amount):
|
|
return Color(Gloss.lerp(colorfrom.r, colorto.r, amount), Gloss.lerp(colorfrom.g, colorto.g, amount), Gloss.lerp(colorfrom.b, colorto.b, amount), Gloss.lerp(colorfrom.a, colorto.a, amount))
|
|
|
|
@staticmethod
|
|
def lerp2(colorfrom, colorto, amount):
|
|
return Color(Gloss.lerp2(colorfrom.r, colorto.r, amount), Gloss.lerp2(colorfrom.g, colorto.g, amount), Gloss.lerp2(colorfrom.b, colorto.b, amount), Gloss.lerp2(colorfrom.a, colorto.a, amount))
|
|
|
|
@staticmethod
|
|
def multi_lerp(values, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
if result == 1.0:
|
|
return values[len(values) - 1]
|
|
|
|
num_items = len(values)
|
|
position = int(math.floor(amount * (num_items - 1))) # what was the minimum item we were up to?
|
|
individual_lerp = 1.0 / (num_items - 1) # how much is each step?
|
|
current_lerp = amount - position * individual_lerp # how far through the current step are we?
|
|
lerp_amount = current_lerp / individual_lerp # how much is that compared to a full step
|
|
|
|
return Color.lerp(values[position], values[position + 1], lerp_amount)
|
|
|
|
@staticmethod
|
|
def smooth_step(colorfrom, colorto, amount):
|
|
return Color(Gloss.smooth_step(colorfrom.r, colorto.r, amount), Gloss.smooth_step(colorfrom.g, colorto.g, amount), Gloss.smooth_step(colorfrom.b, colorto.b, amount), Gloss.smooth_step(colorfrom.a, colorto.a, amount))
|
|
|
|
@staticmethod
|
|
def smooth_step2(colorfrom, colorto, amount):
|
|
return Color(Gloss.smooth_step2(colorfrom.r, colorto.r, amount), Gloss.smooth_step2(colorfrom.g, colorto.g, amount), Gloss.smooth_step2(colorfrom.b, colorto.b, amount), Gloss.smooth_step2(colorfrom.a, colorto.a, amount))
|
|
|
|
|
|
class Point(object):
|
|
@staticmethod
|
|
def add(point1, point2):
|
|
return (point1[0] + point2[0], point1[1] + point2[1])
|
|
|
|
def bounce_both(point1, point2, amount, overshoot = 20):
|
|
return (Gloss.bounce_both(point1[0], point2[0], amount, overshoot), Gloss.bounce_both(point1[1], point2[1], amount, overshoot))
|
|
|
|
def bounce_in(point1, point2, amount, overshoot = 20):
|
|
return (Gloss.bounce_in(point1[0], point2[0], amount, overshoot), Gloss.bounce_in(point1[1], point2[1], amount, overshoot))
|
|
|
|
def bounce_out(point1, point2, amount, overshoot = 20):
|
|
return (Gloss.bounce_out(point1[0], point2[0], amount, overshoot), Gloss.bounce_out(point1[1], point2[1], amount, overshoot))
|
|
|
|
@staticmethod
|
|
def distance(point1, point2):
|
|
return math.sqrt(Point.distance_squared(point1, point2))
|
|
|
|
@staticmethod
|
|
def distance_squared(point1, point2):
|
|
return (point1[0] - point2[0]) * (point1[0] - point2[0]) + (point1[1] - point2[1]) * (point1[1] - point2[1])
|
|
|
|
@staticmethod
|
|
def length(point):
|
|
return math.sqrt(Point.length_squared(point))
|
|
|
|
@staticmethod
|
|
def length_squared(point):
|
|
return (point[0] * point[0]) + (point[1] * point[1])
|
|
|
|
@staticmethod
|
|
def lerp(point1, point2, amount):
|
|
return (Gloss.lerp(point1[0], point2[0], amount), Gloss.lerp(point1[1], point2[1], amount))
|
|
|
|
@staticmethod
|
|
def lerp2(point1, point2, amount):
|
|
return (Gloss.lerp2(point1[0], point2[0], amount), Gloss.lerp2(point1[1], point2[1], amount))
|
|
|
|
@staticmethod
|
|
def multi_lerp(values, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
if result == 1.0:
|
|
return values[len(values) - 1]
|
|
|
|
num_items = len(values)
|
|
position = int(math.floor(amount * (num_items - 1))) # what was the minimum item we were up to?
|
|
individual_lerp = 1.0 / (num_items - 1) # how much is each step?
|
|
current_lerp = amount - position * individual_lerp # how far through the current step are we?
|
|
lerp_amount = current_lerp / individual_lerp # how much is that compared to a full step
|
|
|
|
return Point.lerp(values[position], values[position + 1], lerp_amount)
|
|
|
|
@staticmethod
|
|
def multiply(point1, to):
|
|
if to.__class__.__name__ == "tuple":
|
|
return (point1[0] * to[0], point1[1] * to[1])
|
|
else:
|
|
return (point1[0] * to, point1[1] * to)
|
|
|
|
@staticmethod
|
|
def normalize(point):
|
|
length = self.length()
|
|
factor = 1.0 / length
|
|
return (point[0] * factor, point[1] * factor)
|
|
|
|
@staticmethod
|
|
def smooth_step(point1, point2, amount):
|
|
return (Gloss.smooth_step(point1[0], point2[0], amount), Gloss.smooth_step(point1[1], point2[1], amount))
|
|
|
|
@staticmethod
|
|
def smooth_step2(point1, point2, amount):
|
|
return (Gloss.smooth_step2(point1[0], point2[0], amount), Gloss.smooth_step2(point1[1], point2[1], amount))
|
|
|
|
@staticmethod
|
|
def subtract(point1, point2):
|
|
return (point1[0] - point2[0], point1[1] - point2[1])
|
|
|
|
|
|
class Gloss(object):
|
|
VERSION = 0.9
|
|
|
|
PI_OVER_2 = math.pi / 2.0
|
|
PI_OVER_4 = math.pi / 4.0
|
|
TWO_PI = math.pi * 2.0
|
|
|
|
Color.WHITE = Color(1, 1, 1, 1)
|
|
Color.RED = Color(1, 0, 0, 1)
|
|
Color.GREEN = Color(0, 1, 0, 1)
|
|
Color.BLUE = Color(0, 0, 1, 1)
|
|
Color.BLACK = Color(0, 0, 0, 1)
|
|
Color.CORNFLOWER_BLUE = Color(0.392156863, 0.584313725, 0.929411765, 1) # used for clearing the screen to a safe value
|
|
Color.PURPLE = Color(0.5, 0, 0.5, 1) # used for clearing rendertargets to a safe value
|
|
Color.TRANSPARENT_WHITE = Color(1, 1, 1, 0)
|
|
|
|
Point.ZERO = (0, 0)
|
|
|
|
picking = False
|
|
enable_texturing = True
|
|
running_slowly = False
|
|
|
|
total_seconds = 0
|
|
|
|
sprite_click_tracking = False # set to True when any sprite has an OnClick method attached
|
|
last_clicked_sprite = None # the sprite that was clicked most recently
|
|
|
|
joysticks = []
|
|
auto_particle_systems = []
|
|
sprites = []
|
|
|
|
@staticmethod
|
|
def bounce_both(value1, value2, amount, overshoot = 20):
|
|
overshoot *= 0.25949
|
|
|
|
value2 -= value1
|
|
amount /= 0.5
|
|
|
|
if (amount < 1):
|
|
return value2 / 2 * (amount * amount * ((overshoot + 1) * amount - overshoot)) + value1
|
|
else:
|
|
amount -= 2
|
|
return value2 / 2 * (amount * amount * ((overshoot + 1) * amount + overshoot) + 2) + value1
|
|
|
|
@staticmethod
|
|
def bounce_in(value1, value2, amount, overshoot = 20):
|
|
overshoot *= 0.170158
|
|
value2 -= value1
|
|
|
|
return value2 * amount * amount * ((overshoot + 1) * amount - overshoot) + value1;
|
|
|
|
@staticmethod
|
|
def bounce_out(value1, value2, amount, overshoot = 20):
|
|
overshoot *= 0.170158
|
|
amount = amount / 1.0 - 1
|
|
value2 -= value1
|
|
return value2 * (amount * amount * ((overshoot + 1) * amount + overshoot) + 1) + value1;
|
|
|
|
@staticmethod
|
|
def draw_box(position = (0, 0), width = 128, height = 128, rotation = 0.0, origin = (0, 0), scale = 1, color = Color.WHITE):
|
|
glPushMatrix()
|
|
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
|
boxwidth = width * scale
|
|
boxheight = height * scale
|
|
|
|
if origin is None:
|
|
originx = boxwidth / 2
|
|
originy = boxheight / 2
|
|
else:
|
|
originx = origin[0] * scale
|
|
originy = origin[1] * scale
|
|
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
glBegin(GL_QUADS)
|
|
glVertex2f(-originx, boxheight - originy)
|
|
glVertex2f(boxwidth - originx, boxheight - originy)
|
|
glVertex2f(boxwidth - originx, -originy)
|
|
glVertex2f(-originx, -originy)
|
|
|
|
glEnd()
|
|
|
|
glEnable(GL_TEXTURE_2D)
|
|
glPopMatrix()
|
|
|
|
@staticmethod
|
|
def draw_line(start, finish, color = Color.WHITE, width = 1.0):
|
|
glPushMatrix()
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
glLineWidth(width)
|
|
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
|
glBegin(GL_LINES)
|
|
glVertex2f(start[0], start[1])
|
|
glVertex2f(finish[0], finish[1])
|
|
glEnd()
|
|
|
|
glEnable(GL_TEXTURE_2D)
|
|
glPopMatrix()
|
|
|
|
@staticmethod
|
|
def draw_lines(lines, color = Color.WHITE, width = 1.0, join = False):
|
|
glPushMatrix()
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
glLineWidth(width)
|
|
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
|
if join:
|
|
glBegin(GL_LINE_LOOP)
|
|
else:
|
|
glBegin(GL_LINE_STRIP)
|
|
|
|
for line in lines:
|
|
glVertex2f(line[0], line[1])
|
|
|
|
glEnd()
|
|
|
|
glEnable(GL_TEXTURE_2D)
|
|
glPopMatrix()
|
|
|
|
@staticmethod
|
|
def draw_triangle(points = [(0, 0), (-50, 100), (50, 100)], position = (0,0), rotation = 0.0, origin = (0, 0), scale = 1, color = Color.WHITE):
|
|
glPushMatrix()
|
|
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
glDisable(GL_TEXTURE_2D)
|
|
glDisable(GL_CULL_FACE) # don't give people the hassle of thinking about clockwise vs anti-clockwise
|
|
|
|
if origin is None:
|
|
x1 = points[0][0]
|
|
x2 = points[1][0]
|
|
x3 = points[2][0]
|
|
y1 = points[0][1]
|
|
y2 = points[1][1]
|
|
y3 = points[2][1]
|
|
|
|
originx = (x1 + x2 + x3) / 3.0
|
|
originy = (y1 + y2 + y3) / 3.0
|
|
else:
|
|
originx = origin[0]
|
|
originy = origin[1]
|
|
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
glBegin(GL_TRIANGLES)
|
|
glVertex2f((points[0][0] - originx) * scale, (points[0][1] - originy) * scale)
|
|
glVertex2f((points[1][0] - originx) * scale, (points[1][1] - originy) * scale)
|
|
glVertex2f((points[2][0] - originx) * scale, (points[2][1] - originy) * scale)
|
|
glEnd()
|
|
|
|
glEnable(GL_TEXTURE_2D)
|
|
glEnable(GL_CULL_FACE)
|
|
glPopMatrix()
|
|
|
|
@staticmethod
|
|
def fill(texture = None, color = Color.WHITE, top = Color.CORNFLOWER_BLUE, bottom = Color.CORNFLOWER_BLUE, vertical = True):
|
|
# never draw backgrounds when picking
|
|
if Gloss.picking:
|
|
return
|
|
|
|
if texture is not None:
|
|
width = Gloss.viewport_size[0]
|
|
height = Gloss.viewport_size[1]
|
|
|
|
glPushMatrix()
|
|
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
glBindTexture(GL_TEXTURE_2D, texture.surface)
|
|
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glTexCoord2f(0, texture.height_ratio); glVertex2f(0, height)
|
|
glTexCoord2f(texture.width_ratio, texture.height_ratio); glVertex2f(width, height)
|
|
glTexCoord2f(0, 1); glVertex2f(0, 0)
|
|
glTexCoord2f(texture.width_ratio, 1); glVertex2f(width, 0)
|
|
glEnd()
|
|
|
|
glPopMatrix()
|
|
else:
|
|
# if we're still here, draw the polygon without texture using gradient colors
|
|
if Gloss.enable_texturing:
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
|
width = Gloss.viewport_size[0]
|
|
height = Gloss.viewport_size[1]
|
|
|
|
glPushMatrix()
|
|
|
|
if vertical:
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glColor4f(bottom.r, bottom.g, bottom.b, bottom.a); glVertex2f(0, height)
|
|
glColor4f(bottom.r, bottom.g, bottom.b, bottom.a); glVertex2f(width, height)
|
|
glColor4f(top.r, top.g, top.b, top.a); glVertex2f(0, 0)
|
|
glColor4f(top.r, top.g, top.b, top.a); glVertex2f(width, 0)
|
|
glEnd()
|
|
else:
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glColor4f(top.r, top.g, top.b, top.a); glVertex2f(0, height)
|
|
glColor4f(bottom.r, bottom.g, bottom.b, bottom.a); glVertex2f(width, height)
|
|
glColor4f(top.r, top.g, top.b, top.a); glVertex2f(0, 0)
|
|
glColor4f(bottom.r, bottom.g, bottom.b, bottom.a); glVertex2f(width, 0)
|
|
glEnd()
|
|
|
|
if Gloss.enable_texturing:
|
|
glEnable(GL_TEXTURE_2D)
|
|
|
|
glPopMatrix()
|
|
|
|
@staticmethod
|
|
def clamp(value, minval, maxval):
|
|
if value > maxval:
|
|
value = maxval
|
|
elif value < minval:
|
|
value = minval
|
|
|
|
return value
|
|
|
|
@staticmethod
|
|
def clear(color = Color.CORNFLOWER_BLUE):
|
|
glClearColor(color.r, color.g, color.b, color.a)
|
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
|
|
|
|
@staticmethod
|
|
def hermite(value1, tangent1, value2, tangent2, amount):
|
|
AmountSquared = amount * amount
|
|
AmountCubed = AmountSquared * amount
|
|
|
|
|
|
if amount is 0.0:
|
|
result = value1
|
|
elif amount is 1.0:
|
|
result = value2
|
|
else:
|
|
result = (2 * value1 - 2 * value2 + tangent2 + tangent1) * AmountCubed + (3 * value2 - 3 * value1 - 2 * tangent1 - tangent2) * AmountSquared + tangent1 * amount + value1
|
|
|
|
return result
|
|
|
|
@staticmethod
|
|
def lerp(value1, value2, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
return value1 + (value2 - value1) * result
|
|
|
|
@staticmethod
|
|
def lerp2(value1, value2, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
|
|
if result > 0.5:
|
|
return value2 + (value1 - value2) * ((result - 0.5) * 2)
|
|
else:
|
|
return value1 + (value2 - value1) * (result * 2)
|
|
|
|
@staticmethod
|
|
def multi_lerp(values, amount):
|
|
# this method lets people lerp through multiple numbers, eg (0, 1, 2) with
|
|
# lerp amount 0.5 would return 1. It works by figuring out where in the values list
|
|
# we're up to, seeing how far we've moved towards the next position.
|
|
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
if result == 1.0:
|
|
return values[len(values) - 1]
|
|
|
|
num_items = len(values)
|
|
position = int(math.floor(amount * (num_items - 1))) # what was the minimum item we were up to?
|
|
individual_lerp = 1.0 / (num_items - 1) # how much is each step?
|
|
current_lerp = amount - position * individual_lerp # how far through the current step are we?
|
|
lerp_amount = current_lerp / individual_lerp # how much is that compared to a full step
|
|
|
|
return Gloss.lerp(values[position], values[position + 1], lerp_amount)
|
|
|
|
@staticmethod
|
|
def next_po2(value):
|
|
logbase2 = (math.log(value) / math.log(2))
|
|
return int(round(pow(2, math.ceil(logbase2))))
|
|
|
|
@staticmethod
|
|
def smooth_step(value1, value2, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
result = Gloss.hermite(value1, 0.0, value2, 0.0, result)
|
|
return result
|
|
|
|
@staticmethod
|
|
def smooth_step2(value1, value2, amount):
|
|
result = Gloss.clamp(amount, 0.0, 1.0)
|
|
|
|
if result > 0.5:
|
|
result = Gloss.hermite(value2, 0.0, value1, 0.0, (result - 0.5) * 2)
|
|
else:
|
|
result = Gloss.hermite(value1, 0.0, value2, 0.0, result * 2)
|
|
|
|
return result
|
|
|
|
@staticmethod
|
|
def rand_float(value1, value2):
|
|
return random.random() * (value2 - value1) + value1
|
|
|
|
|
|
@staticmethod
|
|
def save_screenshot(filename):
|
|
if Gloss.redraw_needed:
|
|
Gloss.active_game.gloss_internal_draw()
|
|
|
|
glFinish()
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 4)
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0)
|
|
glPixelStorei(GL_PACK_SKIP_ROWS, 0)
|
|
glPixelStorei(GL_PACK_SKIP_PIXELS, 0)
|
|
|
|
data = glReadPixels(0, 0, Gloss.viewport_size[0], Gloss.viewport_size[1], GL_RGBA, GL_UNSIGNED_BYTE)
|
|
|
|
surface = pygame.image.fromstring(data, (Gloss.viewport_size[0], Gloss.viewport_size[1]), 'RGBA', 1)
|
|
pygame.image.save(surface, filename)
|
|
|
|
@staticmethod
|
|
def set_scene_tint(color):
|
|
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ((color.r, color.g, color.b, color.a)) )
|
|
|
|
@staticmethod
|
|
def to_radians(degrees):
|
|
return degrees * 0.017453292519943295769236907685
|
|
|
|
@staticmethod
|
|
def enable_picking():
|
|
Gloss.picking = True
|
|
glDisable(GL_LIGHTING)
|
|
glDisable(GL_TEXTURE_2D)
|
|
Gloss.enable_texturing = False
|
|
|
|
@staticmethod
|
|
def disable_picking():
|
|
Gloss.picking = False
|
|
glEnable(GL_LIGHTING)
|
|
glEnable(GL_TEXTURE_2D)
|
|
Gloss.enable_texturing = True
|
|
|
|
@staticmethod
|
|
def select_object(pos):
|
|
Gloss.enable_picking()
|
|
Gloss.active_game.gloss_internal_draw()
|
|
|
|
pixels = glReadPixelsub(pos[0], Gloss.viewport_size[1] - pos[1], 1, 1, GL_RGBA)
|
|
|
|
r = pixels[0][0][0]
|
|
g = pixels[0][0][1]
|
|
b = pixels[0][0][2]
|
|
|
|
Gloss.disable_picking()
|
|
|
|
for sprite in Gloss.sprites:
|
|
if (sprite.pick_color[0] == r) and sprite.pick_color[1] == g and sprite.pick_color[2] == b:
|
|
if sprite._on_click is not None:
|
|
# a function has been attached here - call it!
|
|
sprite._on_click(sprite)
|
|
|
|
Gloss.last_clicked_sprite = sprite
|
|
|
|
return sprite
|
|
|
|
return None
|
|
|
|
|
|
class Texture(object):
|
|
def __init__(self, source):
|
|
self.surface = None
|
|
|
|
if (source.__class__.__name__ == "str") or (source.__class__.__name__ == "unicode"):
|
|
try:
|
|
surface = pygame.image.load(source)
|
|
except:
|
|
print("Unable to load texture " + source + ".")
|
|
sys.exit(1)
|
|
else:
|
|
surface = source
|
|
|
|
width = surface.get_width()
|
|
height = surface.get_height()
|
|
|
|
self.width = width
|
|
self.height = height
|
|
self.half_width = self.width / 2
|
|
self.half_height = self.height / 2
|
|
|
|
po2width = Gloss.next_po2(width)
|
|
po2height = Gloss.next_po2(height)
|
|
|
|
if (po2width > Gloss.MaxTextureSize or po2height > Gloss.MaxTextureSize):
|
|
print "Fatal error: texture at " + path + " is bigger than the maximum supported texture size of " + str(Gloss.MaxTextureSize)
|
|
sys.exit(1)
|
|
|
|
self.width_ratio = (self.width / po2width)
|
|
self.height_ratio = 1 - (self.height / po2height)
|
|
|
|
if (width != po2width or height != po2height):
|
|
tmpsurface = pygame.Surface((po2width, po2height), SRCALPHA, 32)
|
|
tmpsurface.blit(surface, (0,0))
|
|
surface = tmpsurface
|
|
|
|
data = pygame.image.tostring(surface, "RGBA", 1)
|
|
|
|
self.surface = glGenTextures(1)
|
|
glBindTexture(GL_TEXTURE_2D, self.surface)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, po2width, po2height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
|
|
|
|
def __del__(self):
|
|
if self.surface is not None:
|
|
# before we try freeing this memory, be careful: Python may have unloaded the OpenGL module by now, in which case just bail out
|
|
if glDeleteTextures is not None:
|
|
glDeleteTextures(self.surface)
|
|
self.surface = None
|
|
|
|
def draw(self, position = (0, 0), rotation = 0.0, origin = (0, 0), scale = 1, color = Color.WHITE):
|
|
# never draw textures if picking
|
|
if Gloss.picking:
|
|
return
|
|
|
|
texwidth = self.width * scale
|
|
texheight = self.height * scale
|
|
|
|
if origin is None:
|
|
originx = texwidth / 2
|
|
originy = texheight / 2
|
|
else:
|
|
originx = origin[0] * scale
|
|
originy = origin[1] * scale
|
|
|
|
glPushMatrix()
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
|
|
glBindTexture(GL_TEXTURE_2D, self.surface)
|
|
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glTexCoord2f(0, self.height_ratio); glVertex2f(-originx, texheight - originy)
|
|
glTexCoord2f(self.width_ratio, self.height_ratio); glVertex2f(texwidth - originx, texheight - originy)
|
|
glTexCoord2f(0, 1); glVertex2f(-originx, -originy)
|
|
glTexCoord2f(self.width_ratio, 1); glVertex2f(texwidth - originx, -originy)
|
|
glEnd()
|
|
|
|
glPopMatrix()
|
|
|
|
class Sprite(object):
|
|
next_id = 1
|
|
pick_r = 0
|
|
pick_g = 0
|
|
pick_b = 0
|
|
|
|
def __init__(self, texture, position = None):
|
|
self.id = str(Sprite.next_id)
|
|
Sprite.next_id += 1
|
|
|
|
self.texture = texture
|
|
|
|
if position is None:
|
|
self.position = (0, 0)
|
|
else:
|
|
self.position = position
|
|
|
|
Sprite.pick_r += 1
|
|
if Sprite.pick_r > 255:
|
|
Sprite.pick_r = 0
|
|
Sprite.pick_g += 1
|
|
if Sprite.pick_g > 255:
|
|
Sprite.pick_g = 0
|
|
Sprite.pick_b += 1
|
|
|
|
self.pick_color = (Sprite.pick_r, Sprite.pick_g, Sprite.pick_b, 255)
|
|
|
|
self._on_click = None
|
|
|
|
Gloss.sprites.append(weakref.ref(self))
|
|
|
|
def __del__(self):
|
|
# remove any dead weak references from the sprite list
|
|
for i in reversed(range(len(Gloss.sprites))):
|
|
if Gloss.sprites[i]() is None:
|
|
del Gloss.sprites[i]
|
|
|
|
def get_on_click(self):
|
|
return self._on_click
|
|
|
|
def set_on_click(self, value):
|
|
self._on_click = value
|
|
Gloss.sprite_click_tracking = True
|
|
|
|
on_click = property(get_on_click, set_on_click)
|
|
|
|
|
|
def draw(self, position = None, rotation = 0.0, origin = (0, 0), scale = 1, color = Color.WHITE):
|
|
texwidth = self.texture.width * scale
|
|
texheight = self.texture.height * scale
|
|
|
|
if origin is None:
|
|
originx = texwidth / 2
|
|
originy = texheight / 2
|
|
else:
|
|
originx = origin[0] * scale
|
|
originy = origin[1] * scale
|
|
|
|
if position is None:
|
|
position = self.position
|
|
|
|
glPushMatrix()
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
if Gloss.picking is False:
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
else:
|
|
glColor3ub(self.pick_color[0], self.pick_color[1], self.pick_color[2])
|
|
|
|
glBindTexture(GL_TEXTURE_2D, self.texture.surface)
|
|
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glTexCoord2f(0, self.texture.height_ratio); glVertex2f(-originx, texheight - originy)
|
|
glTexCoord2f(self.texture.width_ratio, self.texture.height_ratio); glVertex2f(texwidth - originx, texheight - originy)
|
|
glTexCoord2f(0, 1); glVertex2f(-originx, -originy)
|
|
glTexCoord2f(self.texture.width_ratio, 1); glVertex2f(texwidth - originx, -originy)
|
|
glEnd()
|
|
|
|
glPopMatrix()
|
|
|
|
def move(self, x, y):
|
|
self.position = (self.position[0] + x, self.position[1] + y)
|
|
|
|
def move_to(self, x = None, y = None):
|
|
if x is None:
|
|
if y is None:
|
|
return
|
|
|
|
self.position = (self.position[0], y)
|
|
return
|
|
|
|
if y is None:
|
|
self.position = (x, self.position[1])
|
|
else:
|
|
self.position = (x, y)
|
|
|
|
class ParticleSystem(object):
|
|
additive = False
|
|
|
|
def __init__(self, texture, position = None, lifespan = -1, creationspeed = None, initialparticles = 50, particlelifespan = 1000, minspeed = 50, maxspeed = 250, minrotation = 0, maxrotation = 0, minscale = 1.0, maxscale = 1.0, growth = 0.0, wind = None, drag = None, startcolor = Color.WHITE, endcolor = Color.TRANSPARENT_WHITE, onfinish = None, name = ""):
|
|
Gloss.auto_particle_systems.append(self)
|
|
|
|
self.name = name
|
|
self.on_finish = onfinish
|
|
|
|
self.texture = texture
|
|
|
|
if position is None:
|
|
self.position = (0, 0)
|
|
else:
|
|
self.position = position
|
|
|
|
self.created_time = Gloss.tick_count
|
|
self.last_particle_created_time = Gloss.tick_count
|
|
|
|
self.frozen = False # used to stop particle systems being automatically updated
|
|
self.alive = True # set to be False when destroyed
|
|
|
|
self.lifespan = lifespan
|
|
self.creation_speed = creationspeed
|
|
self.initial_particles = initialparticles
|
|
self.particle_lifespan = particlelifespan
|
|
|
|
self.start_color = startcolor
|
|
self.end_color = endcolor
|
|
self.particle_speed_min = minspeed
|
|
self.particle_speed_max = maxspeed
|
|
self.particle_rotation_min = minrotation
|
|
self.particle_rotation_max = maxrotation
|
|
self.particle_scale_min = minscale
|
|
self.particle_scale_max = maxscale
|
|
self.particle_growth = growth
|
|
self.particle_drag = drag
|
|
self.particle_wind = wind
|
|
|
|
self.particles = []
|
|
|
|
for i in range(initialparticles):
|
|
self.create_particle()
|
|
|
|
def __del__(self):
|
|
self.particles = [] # destroy all particles now
|
|
|
|
def draw(self):
|
|
if self.alive is False:
|
|
return
|
|
|
|
# never draw particle systems if picking
|
|
if Gloss.picking:
|
|
return
|
|
|
|
if self.additive:
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
|
|
|
for particle in self.particles:
|
|
self.texture.draw(position = particle.position, rotation = particle.rotation, origin = None, scale = particle.scale + (particle.anim_pos * self.particle_growth), color = particle.color)
|
|
|
|
if self.additive:
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
def update(self):
|
|
# kill off old particle systems
|
|
if (self.lifespan == -1):
|
|
# long-term particle systems also need to be auto-removed if they haven't done anything for a little while
|
|
if len(self.particles) == 0:
|
|
return False
|
|
else:
|
|
# if we have finished our life and all our particles are dead, return false so that the game can remove us from the list of particle systems
|
|
if self.created_time + self.lifespan < Gloss.tick_count:
|
|
return False
|
|
|
|
|
|
# do we need to create a new particle?
|
|
if (self.creation_speed is not None and self.last_particle_created_time + self.creation_speed < Gloss.tick_count):
|
|
self.create_particle()
|
|
|
|
#cache the current wind and drag amounts to save calculation in the particle loop
|
|
if self.particle_wind is not None:
|
|
current_wind = Point.multiply(self.particle_wind, Gloss.elapsed_seconds)
|
|
|
|
if self.particle_drag is not None:
|
|
current_drag = Gloss.elapsed_seconds * self.particle_drag
|
|
|
|
for i in reversed(range(len(self.particles))):
|
|
particle = self.particles[i]
|
|
|
|
if particle.created_time + self.particle_lifespan < Gloss.tick_count:
|
|
del self.particles[i]
|
|
|
|
particle.position = Point.add(particle.position, Point.multiply(particle.velocity, Gloss.elapsed_seconds))
|
|
|
|
if self.particle_wind is not None:
|
|
particle.position = Point.add(particle.position, current_wind)
|
|
|
|
particle.anim_pos = Gloss.clamp((Gloss.tick_count - particle.created_time) / self.particle_lifespan, 0.0, 1.0)
|
|
particle.color = Color.lerp(self.start_color, self.end_color, particle.anim_pos)
|
|
|
|
if self.particle_drag is not None:
|
|
particle.velocity = Point.subtract(particle.velocity, Point.multiply(particle.velocity, current_drag))
|
|
|
|
if (Point.length_squared(particle.velocity) <= 0):
|
|
particle.velocity = Point.ZERO
|
|
|
|
return True
|
|
|
|
def create_particle(self):
|
|
particle = Particle()
|
|
|
|
particle.created_time = Gloss.tick_count
|
|
particle.position = self.position
|
|
particle.rotation = Gloss.rand_float(self.particle_rotation_min, self.particle_rotation_max)
|
|
particle.scale = Gloss.rand_float(self.particle_scale_min, self.particle_scale_max)
|
|
|
|
angle = random.random() * Gloss.TWO_PI
|
|
particle.velocity = Point.multiply((math.cos(angle), math.sin(angle)), Gloss.rand_float(self.particle_speed_min, self.particle_speed_max))
|
|
|
|
self.last_particle_created_time = Gloss.tick_count
|
|
|
|
self.particles.append(particle)
|
|
|
|
class Particle:
|
|
def __init__(self):
|
|
self.anim_pos = 0.0
|
|
self.color = Color.WHITE
|
|
|
|
|
|
class RenderTarget:
|
|
def __init__(self, width = 512, height = 512):
|
|
self.buffer = None
|
|
self.surface = None
|
|
self.width = width
|
|
self.height = height
|
|
self.half_width = width / 2.0
|
|
self.half_height = height / 2.0
|
|
|
|
# create the framebuffer
|
|
temp = glGenFramebuffersEXT(1)
|
|
self.buffer = temp
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self.buffer)
|
|
|
|
# create a texture for rendering
|
|
temp = glGenTextures(1)
|
|
self.surface = temp
|
|
|
|
glActiveTexture(GL_TEXTURE0)
|
|
glBindTexture(GL_TEXTURE_2D, self.surface)
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, None)
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, self.surface, 0)
|
|
|
|
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)
|
|
if status != GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
return
|
|
|
|
Gloss.clear(Color.PURPLE)
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0) # we don't need this activated yet, so unbind it for now
|
|
|
|
# start rendering to this framebuffer
|
|
def activate(self):
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self.buffer)
|
|
Gloss.viewport_size = self.width, self.height
|
|
glViewport(0, 0, self.width, self.height)
|
|
|
|
glMatrixMode(GL_PROJECTION)
|
|
glPushMatrix()
|
|
glLoadIdentity()
|
|
glOrtho(0, self.width, self.height, 0, -100, 100)
|
|
glMatrixMode(GL_MODELVIEW)
|
|
glPushMatrix()
|
|
glLoadIdentity()
|
|
|
|
# stop rendering to this framebuffer
|
|
def deactivate(self):
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)
|
|
Gloss.viewport_size = Gloss.screen_resolution
|
|
glViewport(0, 0, Gloss.screen_resolution[0], Gloss.screen_resolution[1])
|
|
|
|
glMatrixMode(GL_PROJECTION)
|
|
glPopMatrix()
|
|
glMatrixMode(GL_MODELVIEW)
|
|
glPopMatrix()
|
|
|
|
|
|
# destroy this framebuffer and free allocated resources.
|
|
def __del__(self):
|
|
glDeleteFramebuffersEXT(1, [self.buffer])
|
|
glDeleteTextures(1, [self.surface])
|
|
self.buffer = None
|
|
self.surface = None
|
|
|
|
# draw the contents of the buffer to the screen
|
|
def draw(self, position, width = None, height = None, rotation = 0.0, origin = (0, 0), scale = 1, color = Color.WHITE):
|
|
# always draw this texture, even when picking
|
|
glEnable(GL_TEXTURE_2D)
|
|
|
|
if width is None: width = self.width
|
|
if height is None: height = self.height
|
|
|
|
texheight = height * scale
|
|
texwidth = width * scale
|
|
|
|
if origin is None:
|
|
originx = texwidth / 2
|
|
originy = texheight / 2
|
|
else:
|
|
originx = origin[0] * scale
|
|
originy = origin[1] * scale
|
|
|
|
glPushMatrix()
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
if Gloss.picking is False:
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
else:
|
|
# when picking, use pure white so that all textures inside this are visible
|
|
glColor4f(1, 1, 1, 1)
|
|
|
|
glBindTexture(GL_TEXTURE_2D, self.surface)
|
|
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glTexCoord2f(0, 0); glVertex2f(-originx, texheight - originy)
|
|
glTexCoord2f(1, 0); glVertex2f(texwidth - originx, texheight - originy)
|
|
glTexCoord2f(0, 1); glVertex2f(-originx, -originy)
|
|
glTexCoord2f(1, 1); glVertex2f(texwidth - originx, -originy)
|
|
glEnd()
|
|
|
|
# disable texturing if we're picking
|
|
if Gloss.enable_texturing is False:
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
|
glPopMatrix()
|
|
|
|
class SpriteFont(object):
|
|
def __init__(self, filename, size = 18, bold = False, underline = False, startcharacter = 32, endcharacter = 126):
|
|
self.font = pygame.font.Font(filename, size)
|
|
self.font.set_underline(bold)
|
|
self.font.set_bold(underline)
|
|
|
|
self.characters = {}
|
|
|
|
for letter in range(startcharacter, endcharacter + 1):
|
|
self.characters[chr(letter)] = SpriteFontLetter(self.font, chr(letter))
|
|
|
|
self.line_height = self.characters["A"].height
|
|
|
|
def draw(self, text = "Hello, Gloss!", position = (0, 0), rotation = 0.0, scale = 1.0, color = Color.WHITE, letterspacing = 0, linespacing = 0):
|
|
# don't draw text when picking
|
|
if Gloss.picking:
|
|
return
|
|
|
|
currentwidth = 0 # used to reset to the start of the line when breaking
|
|
|
|
if position is None:
|
|
position = self.position
|
|
|
|
letterspacing *= scale
|
|
linespacing *= scale
|
|
|
|
glPushMatrix()
|
|
glTranslatef(position[0], position[1], 0)
|
|
|
|
if rotation is not 0.0:
|
|
glRotatef(rotation, 0, 0, 1)
|
|
|
|
glColor4f(color.r, color.g, color.b, color.a)
|
|
|
|
lines = text.splitlines()
|
|
|
|
for line in lines:
|
|
for letter in line:
|
|
lettertexture = self.characters[letter]
|
|
|
|
texheight = lettertexture.height * scale
|
|
texwidth = lettertexture.width * scale
|
|
|
|
glBindTexture(GL_TEXTURE_2D, lettertexture.surface)
|
|
|
|
glBegin(GL_TRIANGLE_STRIP)
|
|
glTexCoord2f(0, lettertexture.height_ratio); glVertex2f(0, texheight)
|
|
glTexCoord2f(lettertexture.width_ratio, lettertexture.height_ratio); glVertex2f(texwidth, texheight)
|
|
glTexCoord2f(0, 1); glVertex2f(0, 0)
|
|
glTexCoord2f(lettertexture.width_ratio, 1); glVertex2f(texwidth, 0)
|
|
glEnd()
|
|
|
|
glTranslatef(texwidth + letterspacing, 0, 0)
|
|
currentwidth += texwidth + letterspacing
|
|
|
|
# carriage return + line feed
|
|
glTranslatef(-currentwidth, texheight + linespacing, 0)
|
|
currentwidth = 0
|
|
|
|
glPopMatrix()
|
|
|
|
def measure_string(self, text, scale = 1.0, letterspacing = 0, linespacing = 0):
|
|
currentwidth = 0 # used to track the width of the current line
|
|
maxwidth = 0 # used to track the width of the longest line
|
|
maxheight = 0 # used to track the height of the longest line
|
|
|
|
letterspacing *= scale
|
|
linespacing *= scale
|
|
|
|
lines = text.splitlines()
|
|
|
|
for line in lines:
|
|
for letter in line:
|
|
lettertexture = self.characters[letter]
|
|
texheight = lettertexture.height * scale
|
|
texwidth = lettertexture.width * scale
|
|
currentwidth += texwidth + letterspacing
|
|
|
|
# carriage return + line feed
|
|
if currentwidth > maxwidth:
|
|
maxwidth = currentwidth
|
|
|
|
currentwidth = 0
|
|
|
|
maxheight += texheight + linespacing
|
|
|
|
return maxwidth,maxheight
|
|
|
|
class SpriteFontLetter(object):
|
|
def __init__(self, font, letter):
|
|
surface = font.render(letter, True, (255,255,255))
|
|
|
|
width = surface.get_width()
|
|
height = surface.get_height()
|
|
|
|
self.width = width
|
|
self.height = height
|
|
self.half_width = self.width / 2
|
|
self.half_height = self.height / 2
|
|
|
|
po2width = Gloss.next_po2(width)
|
|
po2height = Gloss.next_po2(height)
|
|
|
|
if (po2width > Gloss.MaxTextureSize or po2height > Gloss.MaxTextureSize):
|
|
print "Fatal error: texture at " + path + " is bigger than the maximum supported texture size of " + str(Gloss.MaxTextureSize)
|
|
sys.exit(1)
|
|
|
|
self.width_ratio = (self.width / po2width)
|
|
self.height_ratio = 1 - (self.height / po2height)
|
|
|
|
if (width != po2width or height != po2height):
|
|
tmpsurface = pygame.Surface((po2width, po2height), SRCALPHA, 32)
|
|
tmpsurface.blit(surface, (0,0))
|
|
surface = tmpsurface
|
|
|
|
letter = pygame.image.tostring(surface, 'RGBA', 1)
|
|
|
|
self.surface = glGenTextures(1)
|
|
glBindTexture(GL_TEXTURE_2D, self.surface)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, po2width, po2height, 0, GL_RGBA, GL_UNSIGNED_BYTE, letter)
|