mirror of
https://github.com/quantum5/punyverse.git
synced 2025-04-24 13:11:57 -04:00
Converted info display to use shaders.
This commit is contained in:
parent
4857b5487c
commit
9e65004128
BIN
punyverse/assets/textures/font.png
Normal file
BIN
punyverse/assets/textures/font.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -5,10 +5,10 @@ from pyglet.gl import *
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
|
||||||
from punyverse.glgeom import compile, glRestore, belt, Disk, OrbitVBO, Matrix4f, SimpleSphere, TangentSphere, Cube
|
from punyverse.glgeom import *
|
||||||
from punyverse.model import load_model, WavefrontVBO
|
from punyverse.model import load_model, WavefrontVBO
|
||||||
from punyverse.orbit import KeplerOrbit
|
from punyverse.orbit import KeplerOrbit
|
||||||
from punyverse.texture import get_best_texture, load_clouds, get_cube_map, load_texture_1d
|
from punyverse.texture import get_best_texture, load_alpha_mask, get_cube_map, load_texture_1d
|
||||||
from punyverse.utils import cached_property
|
from punyverse.utils import cached_property
|
||||||
|
|
||||||
G = 6.67384e-11 # Gravitation Constant
|
G = 6.67384e-11 # Gravitation Constant
|
||||||
|
@ -325,7 +325,7 @@ class SphericalBody(Body):
|
||||||
atm_texture = atmosphere_data.get('diffuse_texture', None)
|
atm_texture = atmosphere_data.get('diffuse_texture', None)
|
||||||
cloud_texture = atmosphere_data.get('cloud_texture', None)
|
cloud_texture = atmosphere_data.get('cloud_texture', None)
|
||||||
if cloud_texture is not None:
|
if cloud_texture is not None:
|
||||||
self.cloud_transparency = get_best_texture(cloud_texture, loader=load_clouds)
|
self.cloud_transparency = get_best_texture(cloud_texture, loader=load_alpha_mask)
|
||||||
self.cloud_radius = self.radius + 2
|
self.cloud_radius = self.radius + 2
|
||||||
self.clouds = self._get_sphere(division, tangent=False)
|
self.clouds = self._get_sphere(division, tangent=False)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
from array import array
|
from array import array
|
||||||
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint, c_short, c_ushort
|
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint, c_short, c_ushort
|
||||||
from math import *
|
from math import *
|
||||||
|
@ -10,7 +12,8 @@ from six.moves import range
|
||||||
TWOPI = pi * 2
|
TWOPI = pi * 2
|
||||||
|
|
||||||
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'belt',
|
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'belt',
|
||||||
'glSection', 'glRestore', 'progress_bar']
|
'glSection', 'glRestore', 'glContext', 'progress_bar',
|
||||||
|
'FontEngine', 'Matrix4f', 'Disk', 'OrbitVBO', 'SimpleSphere', 'TangentSphere', 'Cube']
|
||||||
|
|
||||||
|
|
||||||
class glContext(object):
|
class glContext(object):
|
||||||
|
@ -320,6 +323,53 @@ class OrbitVBO(object):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class FontEngine(object):
|
||||||
|
type = GL_SHORT
|
||||||
|
stride = 4 * 2
|
||||||
|
position_offset = 0
|
||||||
|
position_size = 2
|
||||||
|
tex_offset = position_size * 2
|
||||||
|
tex_size = 2
|
||||||
|
|
||||||
|
def __init__(self, max_length=256):
|
||||||
|
self.storage = array('h', max_length * 24 * [0])
|
||||||
|
vbo = GLuint()
|
||||||
|
glGenBuffers(1, byref(vbo))
|
||||||
|
self.vbo = vbo.value
|
||||||
|
self.vertex_count = None
|
||||||
|
|
||||||
|
def draw(self, string):
|
||||||
|
index = 0
|
||||||
|
row = 0
|
||||||
|
col = 0
|
||||||
|
for c in string:
|
||||||
|
if c == '\n':
|
||||||
|
row += 1
|
||||||
|
col = 0
|
||||||
|
continue
|
||||||
|
o = ord(c)
|
||||||
|
if 32 <= o < 128:
|
||||||
|
self.storage[24*index:24*index+24] = array('h', [
|
||||||
|
row, col, o - 32, 1,
|
||||||
|
row + 1, col, o - 32, 0,
|
||||||
|
row + 1, col + 1, o - 31, 0,
|
||||||
|
row, col, o - 32, 1,
|
||||||
|
row + 1, col + 1, o - 31, 0,
|
||||||
|
row, col + 1, o - 31, 1,
|
||||||
|
])
|
||||||
|
index += 1
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
self.vertex_count = index * 6
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, self.storage.itemsize * len(self.storage),
|
||||||
|
array_to_ctypes(self.storage), GL_STREAM_DRAW)
|
||||||
|
|
||||||
|
def end(self):
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
|
||||||
|
|
||||||
def belt(radius, cross, object, count):
|
def belt(radius, cross, object, count):
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
theta = TWOPI * random()
|
theta = TWOPI * random()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -26,21 +27,29 @@ def get_context_info(context):
|
||||||
|
|
||||||
|
|
||||||
class LoaderWindow(pyglet.window.Window):
|
class LoaderWindow(pyglet.window.Window):
|
||||||
|
MONOSPACE = ('Consolas', 'Droid Sans Mono', 'Courier', 'Courier New', 'Dejavu Sans Mono')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LoaderWindow, self).__init__(*args, **kwargs)
|
super(LoaderWindow, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# work around pyglet bug: decoding font names as utf-8 instead of mbcs when using EnumFontsA.
|
||||||
|
stderr = sys.stderr
|
||||||
|
sys.stderr = open(os.devnull, 'w')
|
||||||
|
pyglet.font.have_font(self.MONOSPACE[0])
|
||||||
|
sys.stderr = stderr
|
||||||
|
|
||||||
self.loading_phase = pyglet.text.Label(
|
self.loading_phase = pyglet.text.Label(
|
||||||
font_name='Consolas', font_size=20, x=10, y=self.height - 50,
|
font_name=self.MONOSPACE, font_size=20, x=10, y=self.height - 50,
|
||||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||||
multiline=True, text='Punyverse is starting...'
|
multiline=True, text='Punyverse is starting...'
|
||||||
)
|
)
|
||||||
self.loading_label = pyglet.text.Label(
|
self.loading_label = pyglet.text.Label(
|
||||||
font_name='Consolas', font_size=16, x=10, y=self.height - 120,
|
font_name=self.MONOSPACE, font_size=16, x=10, y=self.height - 120,
|
||||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||||
multiline=True
|
multiline=True
|
||||||
)
|
)
|
||||||
self.info_label = pyglet.text.Label(
|
self.info_label = pyglet.text.Label(
|
||||||
font_name='Consolas', font_size=13, x=10, y=self.height - 220,
|
font_name=self.MONOSPACE, font_size=13, x=10, y=self.height - 220,
|
||||||
color=(255, 255, 255, 255), width=self.width - 20,
|
color=(255, 255, 255, 255), width=self.width - 20,
|
||||||
multiline=True
|
multiline=True
|
||||||
)
|
)
|
||||||
|
|
|
@ -103,6 +103,9 @@ class Program(object):
|
||||||
def uniform_bool(self, name, value):
|
def uniform_bool(self, name, value):
|
||||||
glUniform1i(self.uniforms[name], bool(value))
|
glUniform1i(self.uniforms[name], bool(value))
|
||||||
|
|
||||||
|
def uniform_vec2(self, name, a, b):
|
||||||
|
glUniform2f(self.uniforms[name], a, b)
|
||||||
|
|
||||||
def uniform_vec3(self, name, a, b, c):
|
def uniform_vec3(self, name, a, b, c):
|
||||||
glUniform3f(self.uniforms[name], a, b, c)
|
glUniform3f(self.uniforms[name], a, b, c)
|
||||||
|
|
||||||
|
|
12
punyverse/shaders/text.fragment.glsl
Normal file
12
punyverse/shaders/text.fragment.glsl
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec2 v_uv;
|
||||||
|
|
||||||
|
out vec4 o_fragColor;
|
||||||
|
|
||||||
|
uniform sampler2D u_alpha;
|
||||||
|
uniform vec3 u_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
o_fragColor = vec4(u_color, texture(u_alpha, v_uv).r);
|
||||||
|
}
|
14
punyverse/shaders/text.vertex.glsl
Normal file
14
punyverse/shaders/text.vertex.glsl
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec2 a_rc;
|
||||||
|
in vec2 a_tex;
|
||||||
|
|
||||||
|
out vec2 v_uv;
|
||||||
|
|
||||||
|
uniform mat4 u_projMatrix;
|
||||||
|
uniform vec2 u_start;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_projMatrix * vec4(a_rc.y * 8 + u_start.x, a_rc.x * 16 + u_start.y, 0, 1);
|
||||||
|
v_uv = vec2(a_tex.x * 8 / 1024, a_tex.y);
|
||||||
|
}
|
|
@ -42,7 +42,8 @@ except ImportError:
|
||||||
result[y1 * row:y1 * row + row] = source[y2 * row:y2 * row + row]
|
result[y1 * row:y1 * row + row] = source[y2 * row:y2 * row + row]
|
||||||
return six.binary_type(result)
|
return six.binary_type(result)
|
||||||
|
|
||||||
__all__ = ['load_texture', 'load_clouds', 'load_image', 'get_best_texture', 'max_texture_size', 'get_cube_map']
|
__all__ = ['load_texture', 'load_alpha_mask', 'load_image', 'get_best_texture', 'max_texture_size',
|
||||||
|
'get_cube_map', 'load_texture_1d']
|
||||||
|
|
||||||
id = 0
|
id = 0
|
||||||
cache = {}
|
cache = {}
|
||||||
|
@ -259,11 +260,8 @@ def load_texture_1d(file, clamp=False):
|
||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
def load_clouds(file):
|
def load_alpha_mask(file, clamp=False):
|
||||||
path, file = get_file_path(file)
|
path, file = get_file_path(file)
|
||||||
if path in cache:
|
|
||||||
return cache[path]
|
|
||||||
|
|
||||||
path, width, height, depth, mode, texture = load_image(file, path)
|
path, width, height, depth, mode, texture = load_image(file, path)
|
||||||
|
|
||||||
if depth != 1:
|
if depth != 1:
|
||||||
|
@ -284,10 +282,13 @@ def load_clouds(file):
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||||
|
|
||||||
|
if clamp:
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
||||||
|
|
||||||
if gl_info.have_extension('GL_EXT_texture_filter_anisotropic'):
|
if gl_info.have_extension('GL_EXT_texture_filter_anisotropic'):
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glGetInteger(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT))
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glGetInteger(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT))
|
||||||
|
|
||||||
cache[path] = id
|
|
||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from math import hypot
|
from math import hypot
|
||||||
|
@ -54,8 +55,6 @@ class Punyverse(pyglet.window.Window):
|
||||||
self.key_handler = {}
|
self.key_handler = {}
|
||||||
self.mouse_press_handler = {}
|
self.mouse_press_handler = {}
|
||||||
|
|
||||||
self.label = pyglet.text.Label('', font_name='Consolas', font_size=12, x=10, y=self.height - 20,
|
|
||||||
color=(255,) * 4, multiline=True, width=600)
|
|
||||||
self.exclusive = False
|
self.exclusive = False
|
||||||
self.modifiers = 0
|
self.modifiers = 0
|
||||||
|
|
||||||
|
@ -127,6 +126,8 @@ class Punyverse(pyglet.window.Window):
|
||||||
glEnable(GL_DEPTH_TEST)
|
glEnable(GL_DEPTH_TEST)
|
||||||
glShadeModel(GL_SMOOTH)
|
glShadeModel(GL_SMOOTH)
|
||||||
|
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW)
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
|
||||||
glEnable(GL_LIGHTING)
|
glEnable(GL_LIGHTING)
|
||||||
|
@ -144,6 +145,8 @@ class Punyverse(pyglet.window.Window):
|
||||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1))
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1))
|
||||||
glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1))
|
glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1))
|
||||||
|
|
||||||
|
self.info_engine = FontEngine()
|
||||||
|
|
||||||
pyglet.clock.schedule(self.update)
|
pyglet.clock.schedule(self.update)
|
||||||
self.on_resize(self.width, self.height) # On resize handler does nothing unless it's loaded
|
self.on_resize(self.width, self.height) # On resize handler does nothing unless it's loaded
|
||||||
|
|
||||||
|
@ -213,9 +216,8 @@ class Punyverse(pyglet.window.Window):
|
||||||
if not width or not height:
|
if not width or not height:
|
||||||
# Sometimes this happen for no reason?
|
# Sometimes this happen for no reason?
|
||||||
return
|
return
|
||||||
self.label.y = height - 20
|
|
||||||
glViewport(0, 0, width, height)
|
|
||||||
|
|
||||||
|
glViewport(0, 0, width, height)
|
||||||
glMatrixMode(GL_PROJECTION)
|
glMatrixMode(GL_PROJECTION)
|
||||||
self.world.resize(width, height)
|
self.world.resize(width, height)
|
||||||
glLoadMatrixf(self.world.projection_matrix())
|
glLoadMatrixf(self.world.projection_matrix())
|
||||||
|
@ -262,7 +264,13 @@ class Punyverse(pyglet.window.Window):
|
||||||
width, height = self.get_size()
|
width, height = self.get_size()
|
||||||
|
|
||||||
if self.info:
|
if self.info:
|
||||||
ortho(width, height)
|
projection = Matrix4f([
|
||||||
|
2 / width, 0, 0, 0,
|
||||||
|
0, -2 / height, 0, 0,
|
||||||
|
0, 0, -1, 0,
|
||||||
|
-1, 1, 0, 1,
|
||||||
|
])
|
||||||
|
|
||||||
if self.info_precise:
|
if self.info_precise:
|
||||||
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n'
|
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n'
|
||||||
'Direction(pitch=%.2f, yaw=%.2f, roll=%.2f)\nTick: %d' %
|
'Direction(pitch=%.2f, yaw=%.2f, roll=%.2f)\nTick: %d' %
|
||||||
|
@ -271,8 +279,30 @@ class Punyverse(pyglet.window.Window):
|
||||||
else:
|
else:
|
||||||
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n' %
|
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n' %
|
||||||
(pyglet.clock.get_fps(), c.x, c.y, c.z, self.world.cam.speed, self.get_time_per_second()))
|
(pyglet.clock.get_fps(), c.x, c.y, c.z, self.world.cam.speed, self.get_time_per_second()))
|
||||||
self.label.text = info
|
|
||||||
self.label.draw()
|
glEnable(GL_BLEND)
|
||||||
|
shader = self.world.activate_shader('text')
|
||||||
|
shader.uniform_mat4('u_projMatrix', projection)
|
||||||
|
self.info_engine.draw(info)
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0)
|
||||||
|
glBindTexture(GL_TEXTURE_2D, self.world.font_tex)
|
||||||
|
shader.uniform_texture('u_alpha', 0)
|
||||||
|
shader.uniform_vec3('u_color', 1, 1, 1)
|
||||||
|
shader.uniform_vec2('u_start', 10, 10)
|
||||||
|
|
||||||
|
shader.vertex_attribute('a_rc', self.info_engine.position_size, self.info_engine.type, GL_FALSE,
|
||||||
|
self.info_engine.stride, self.info_engine.position_offset)
|
||||||
|
shader.vertex_attribute('a_tex', self.info_engine.tex_size, self.info_engine.type, GL_FALSE,
|
||||||
|
self.info_engine.stride, self.info_engine.tex_offset)
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, self.info_engine.vertex_count)
|
||||||
|
|
||||||
|
self.info_engine.end()
|
||||||
|
self.world.activate_shader(None)
|
||||||
|
glDisable(GL_BLEND)
|
||||||
|
|
||||||
|
ortho(width, height)
|
||||||
with glRestore(GL_CURRENT_BIT | GL_LINE_BIT):
|
with glRestore(GL_CURRENT_BIT | GL_LINE_BIT):
|
||||||
glLineWidth(2)
|
glLineWidth(2)
|
||||||
cx, cy = width / 2, height / 2
|
cx, cy = width / 2, height / 2
|
||||||
|
|
|
@ -312,6 +312,7 @@
|
||||||
"yaw": -97
|
"yaw": -97
|
||||||
},
|
},
|
||||||
"asteroids": ["asteroids/01.obj", "asteroids/02.obj", "asteroids/03.obj"],
|
"asteroids": ["asteroids/01.obj", "asteroids/02.obj", "asteroids/03.obj"],
|
||||||
|
"font": "font.png",
|
||||||
"start": {
|
"start": {
|
||||||
"z": "AU - 400",
|
"z": "AU - 400",
|
||||||
"yaw": 180
|
"yaw": 180
|
||||||
|
|
|
@ -24,6 +24,7 @@ class World(object):
|
||||||
'star': ('star.vertex.glsl', 'star.fragment.glsl'),
|
'star': ('star.vertex.glsl', 'star.fragment.glsl'),
|
||||||
'ring': ('ring.vertex.glsl', 'ring.fragment.glsl'),
|
'ring': ('ring.vertex.glsl', 'ring.fragment.glsl'),
|
||||||
'atmosphere': ('atmosphere.vertex.glsl', 'atmosphere.fragment.glsl'),
|
'atmosphere': ('atmosphere.vertex.glsl', 'atmosphere.fragment.glsl'),
|
||||||
|
'text': ('text.vertex.glsl', 'text.fragment.glsl'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, file, callback):
|
def __init__(self, file, callback):
|
||||||
|
@ -133,6 +134,8 @@ class World(object):
|
||||||
self.callback('Loading asteroids...', 'Loading %s...' % file, i / len(asteroids))
|
self.callback('Loading asteroids...', 'Loading %s...' % file, i / len(asteroids))
|
||||||
self.asteroids.load(file)
|
self.asteroids.load(file)
|
||||||
|
|
||||||
|
self.font_tex = load_alpha_mask(root['font'], clamp=True)
|
||||||
|
|
||||||
def _body(self, name, info, parent=None):
|
def _body(self, name, info, parent=None):
|
||||||
if 'texture' in info:
|
if 'texture' in info:
|
||||||
body = SphericalBody(name, self, info, parent)
|
body = SphericalBody(name, self, info, parent)
|
||||||
|
|
Loading…
Reference in a new issue