mirror of
https://github.com/quantum5/punyverse.git
synced 2025-04-24 13:11:57 -04:00
Move sky to shaders.
This commit is contained in:
parent
999dc78410
commit
66c54a9dbb
|
@ -16,7 +16,7 @@ class Camera(object):
|
||||||
self.fov = radians(45)
|
self.fov = radians(45)
|
||||||
self.aspect = 1
|
self.aspect = 1
|
||||||
self.znear = 1
|
self.znear = 1
|
||||||
self.zfar = 50000000
|
self.zfar = 3000000
|
||||||
|
|
||||||
self.speed = 0
|
self.speed = 0
|
||||||
self.roll_left = False
|
self.roll_left = False
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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, Sphere, Disk, OrbitVBO, Matrix4f
|
from punyverse.glgeom import compile, glRestore, belt, Sphere, Disk, OrbitVBO, Matrix4f, SimpleSphere
|
||||||
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
|
from punyverse.texture import get_best_texture, load_clouds
|
||||||
|
@ -120,20 +120,30 @@ class Sky(Entity):
|
||||||
|
|
||||||
self.texture = get_best_texture(info['texture'])
|
self.texture = get_best_texture(info['texture'])
|
||||||
division = info.get('division', 30)
|
division = info.get('division', 30)
|
||||||
self.sphere = Sphere(info.get('radius', 1000000), division, division)
|
self.sphere = SimpleSphere(division, division)
|
||||||
|
|
||||||
def draw(self, options):
|
def draw(self, options):
|
||||||
cam = self.world.cam
|
cam = self.world.cam
|
||||||
with glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
|
shader = self.world.activate_shader('sky')
|
||||||
matrix = self.world.view_matrix() * Matrix4f.from_angles((-cam.x, -cam.y, -cam.z), self.rotation)
|
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() *
|
||||||
glLoadMatrixf(matrix.as_gl())
|
Matrix4f.from_angles(rotation=(cam.pitch, cam.yaw, cam.roll)) *
|
||||||
glEnable(GL_CULL_FACE)
|
Matrix4f.from_angles(rotation=self.rotation))
|
||||||
glEnable(GL_TEXTURE_2D)
|
|
||||||
glDisable(GL_LIGHTING)
|
|
||||||
|
|
||||||
glCullFace(GL_FRONT)
|
glActiveTexture(GL_TEXTURE0)
|
||||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||||
self.sphere.draw()
|
shader.uniform_texture('u_skysphere', 0)
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
|
||||||
|
shader.vertex_attribute('a_direction', self.sphere.direction_size, self.sphere.type, GL_FALSE,
|
||||||
|
self.sphere.stride, self.sphere.direction_offset)
|
||||||
|
shader.vertex_attribute('a_uv', self.sphere.uv_size, self.sphere.type, GL_FALSE,
|
||||||
|
self.sphere.stride, self.sphere.uv_offset)
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.sphere.vertex_count)
|
||||||
|
|
||||||
|
shader.deactivate_attributes()
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
self.world.activate_shader(None)
|
||||||
|
|
||||||
|
|
||||||
class Body(Entity):
|
class Body(Entity):
|
||||||
|
|
|
@ -202,6 +202,40 @@ class Disk(object):
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleSphere(object):
|
||||||
|
type = GL_FLOAT
|
||||||
|
stride = 5 * 4
|
||||||
|
direction_offset = 0
|
||||||
|
direction_size = 3
|
||||||
|
uv_offset = direction_size * 4
|
||||||
|
uv_size = 2
|
||||||
|
|
||||||
|
def __init__(self, lats, longs):
|
||||||
|
tau = pi * 2
|
||||||
|
phi_div = tau / longs
|
||||||
|
theta_div = pi / lats
|
||||||
|
|
||||||
|
self.vertex_count = (lats + 1) * (longs + 1) * 2
|
||||||
|
buffer = self.vertex_count * 5 * [0]
|
||||||
|
index = 0
|
||||||
|
reverse = False
|
||||||
|
for i in range(longs + 1):
|
||||||
|
phi1, phi2 = i * phi_div, (i + 1) * phi_div
|
||||||
|
for j in range(lats + 1):
|
||||||
|
theta = j * theta_div
|
||||||
|
if reverse:
|
||||||
|
theta = pi - theta
|
||||||
|
sine = sin(theta)
|
||||||
|
dz = cos(theta)
|
||||||
|
t = 1 - theta / pi
|
||||||
|
buffer[index:index + 10] = [sine * cos(phi2), sine * sin(phi2), dz, phi2 / tau, t,
|
||||||
|
sine * cos(phi1), sine * sin(phi1), dz, phi1 / tau, t]
|
||||||
|
index += 10
|
||||||
|
reverse ^= True
|
||||||
|
|
||||||
|
self.vbo = array_to_gl_buffer(buffer)
|
||||||
|
|
||||||
|
|
||||||
class Sphere(object):
|
class Sphere(object):
|
||||||
def __init__(self, r, lats, longs):
|
def __init__(self, r, lats, longs):
|
||||||
tau = pi * 2
|
tau = pi * 2
|
||||||
|
|
104
punyverse/shader.py
Normal file
104
punyverse/shader.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import os
|
||||||
|
from ctypes import pointer, byref, create_string_buffer, POINTER, cast
|
||||||
|
|
||||||
|
from pyglet.gl import *
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
from six.moves import range
|
||||||
|
|
||||||
|
SHADERS_DIR = os.path.join(os.path.dirname(__file__), 'shaders')
|
||||||
|
|
||||||
|
|
||||||
|
class CompileError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class glShader(object):
|
||||||
|
def __init__(self, type):
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.shader = glCreateShader(self.type)
|
||||||
|
return self.shader
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
glDeleteShader(self.shader)
|
||||||
|
|
||||||
|
|
||||||
|
class Program(object):
|
||||||
|
@classmethod
|
||||||
|
def load_file(cls, file):
|
||||||
|
with open(os.path.join(SHADERS_DIR, file), 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile_shader(cls, shader, source):
|
||||||
|
buffer = create_string_buffer(source)
|
||||||
|
glShaderSource(shader, 1, cast(pointer(pointer(buffer)), POINTER(POINTER(GLchar))), None)
|
||||||
|
glCompileShader(shader)
|
||||||
|
|
||||||
|
succeeded = GLint()
|
||||||
|
log_length = GLint()
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, byref(succeeded))
|
||||||
|
if not succeeded:
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(log_length))
|
||||||
|
buffer = create_string_buffer(log_length.value + 1)
|
||||||
|
glGetShaderInfoLog(shader, log_length.value, None, buffer)
|
||||||
|
raise CompileError(buffer.value)
|
||||||
|
|
||||||
|
def __init__(self, vertex_file, fragment_file):
|
||||||
|
with glShader(GL_VERTEX_SHADER) as vertex_shader, glShader(GL_FRAGMENT_SHADER) as fragment_shader:
|
||||||
|
self.compile_shader(vertex_shader, self.load_file(vertex_file))
|
||||||
|
self.compile_shader(fragment_shader, self.load_file(fragment_file))
|
||||||
|
|
||||||
|
program = glCreateProgram()
|
||||||
|
glAttachShader(program, vertex_shader)
|
||||||
|
glAttachShader(program, fragment_shader)
|
||||||
|
glLinkProgram(program)
|
||||||
|
|
||||||
|
succeeded = GLint()
|
||||||
|
log_length = GLint()
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, byref(succeeded))
|
||||||
|
if not succeeded:
|
||||||
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, byref(log_length))
|
||||||
|
buffer = create_string_buffer(log_length.value + 1)
|
||||||
|
glGetProgramInfoLog(program, log_length.value, None, buffer)
|
||||||
|
glDeleteProgram(program)
|
||||||
|
raise CompileError(buffer.value)
|
||||||
|
|
||||||
|
glDetachShader(program, vertex_shader)
|
||||||
|
glDetachShader(program, fragment_shader)
|
||||||
|
|
||||||
|
self.program = program
|
||||||
|
self.attributes = self._variable_locations(GL_ACTIVE_ATTRIBUTES, glGetActiveAttrib, glGetAttribLocation)
|
||||||
|
self.uniforms = self._variable_locations(GL_ACTIVE_UNIFORMS, glGetActiveUniform, glGetUniformLocation)
|
||||||
|
self.active_attributes = set()
|
||||||
|
|
||||||
|
def vertex_attribute(self, name, size, type, normalized, stride, offset):
|
||||||
|
location = self.attributes[name]
|
||||||
|
glVertexAttribPointer(location, size, type, normalized, stride, offset)
|
||||||
|
glEnableVertexAttribArray(location)
|
||||||
|
self.active_attributes.add(location)
|
||||||
|
|
||||||
|
def deactivate_attributes(self):
|
||||||
|
for location in self.active_attributes:
|
||||||
|
glDisableVertexAttribArray(location)
|
||||||
|
self.active_attributes.clear()
|
||||||
|
|
||||||
|
def uniform_mat4(self, name, matrix):
|
||||||
|
glUniformMatrix4fv(self.uniforms[name], 1, GL_FALSE, matrix.as_gl())
|
||||||
|
|
||||||
|
def uniform_texture(self, name, index):
|
||||||
|
glUniform1i(self.uniforms[name], index)
|
||||||
|
|
||||||
|
def _variable_locations(self, count_type, get_func, loc_func):
|
||||||
|
variables = {}
|
||||||
|
count = GLint()
|
||||||
|
glGetProgramiv(self.program, count_type, byref(count))
|
||||||
|
buffer = create_string_buffer(256)
|
||||||
|
size = GLint()
|
||||||
|
type = GLenum()
|
||||||
|
|
||||||
|
for index in range(count.value):
|
||||||
|
get_func(self.program, index, 256, None, byref(size), byref(type), buffer)
|
||||||
|
variables[buffer.value.decode('ascii')] = loc_func(self.program, buffer)
|
||||||
|
return variables
|
8
punyverse/shaders/sky.fragment.glsl
Normal file
8
punyverse/shaders/sky.fragment.glsl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec2 v_uv;
|
||||||
|
uniform sampler2D u_skysphere;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = vec4(texture(u_skysphere, v_uv).rgb, 1);
|
||||||
|
}
|
11
punyverse/shaders/sky.vertex.glsl
Normal file
11
punyverse/shaders/sky.vertex.glsl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec3 a_direction;
|
||||||
|
in vec2 a_uv;
|
||||||
|
out vec2 v_uv;
|
||||||
|
uniform mat4 u_mvpMatrix;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = (u_mvpMatrix * vec4(a_direction, 1)).xyww;
|
||||||
|
v_uv = a_uv;
|
||||||
|
}
|
|
@ -298,7 +298,6 @@
|
||||||
"sky": {
|
"sky": {
|
||||||
"texture": "sky.jpg",
|
"texture": "sky.jpg",
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"radius": 305000000,
|
|
||||||
"division": 30,
|
"division": 30,
|
||||||
"pitch": 90,
|
"pitch": 90,
|
||||||
"yaw": 30,
|
"yaw": 30,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import six
|
||||||
from punyverse import texture
|
from punyverse import texture
|
||||||
from punyverse.camera import Camera
|
from punyverse.camera import Camera
|
||||||
from punyverse.entity import *
|
from punyverse.entity import *
|
||||||
|
from punyverse.shader import Program
|
||||||
|
|
||||||
|
|
||||||
def load_world(file, callback=lambda message, completion: None):
|
def load_world(file, callback=lambda message, completion: None):
|
||||||
|
@ -16,6 +17,10 @@ def load_world(file, callback=lambda message, completion: None):
|
||||||
|
|
||||||
|
|
||||||
class World(object):
|
class World(object):
|
||||||
|
PROGRAMS = {
|
||||||
|
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, file, callback):
|
def __init__(self, file, callback):
|
||||||
self.tracker = []
|
self.tracker = []
|
||||||
self.x = None
|
self.x = None
|
||||||
|
@ -28,6 +33,9 @@ class World(object):
|
||||||
|
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self._parse(file)
|
self._parse(file)
|
||||||
|
|
||||||
|
self._program = None
|
||||||
|
self.programs = self._load_programs()
|
||||||
del self.callback # So it can't be used after loading finishes
|
del self.callback # So it can't be used after loading finishes
|
||||||
|
|
||||||
self._time_accumulate = 0
|
self._time_accumulate = 0
|
||||||
|
@ -36,6 +44,15 @@ class World(object):
|
||||||
for entity in self.tracker:
|
for entity in self.tracker:
|
||||||
entity.update()
|
entity.update()
|
||||||
|
|
||||||
|
def _load_programs(self):
|
||||||
|
programs = {}
|
||||||
|
count = len(self.PROGRAMS)
|
||||||
|
for i, (name, (vertex, fragment)) in enumerate(six.iteritems(self.PROGRAMS)):
|
||||||
|
self.callback('Loading shaders (%d of %d)...' % (i, count),
|
||||||
|
'Loading shader "%s" (%s, %s).' % (name, vertex, fragment), i / count)
|
||||||
|
programs[name] = Program(vertex, fragment)
|
||||||
|
return programs
|
||||||
|
|
||||||
def evaluate(self, value):
|
def evaluate(self, value):
|
||||||
return eval(str(value), {'__builtins__': None}, self._context)
|
return eval(str(value), {'__builtins__': None}, self._context)
|
||||||
|
|
||||||
|
@ -128,6 +145,7 @@ class World(object):
|
||||||
def update(self, dt, move, tick):
|
def update(self, dt, move, tick):
|
||||||
c = self.cam
|
c = self.cam
|
||||||
c.update(dt, move)
|
c.update(dt, move)
|
||||||
|
self.vp_matrix = None
|
||||||
|
|
||||||
if tick:
|
if tick:
|
||||||
delta = self.tick_length * dt
|
delta = self.tick_length * dt
|
||||||
|
@ -151,6 +169,24 @@ class World(object):
|
||||||
def projection_matrix(self):
|
def projection_matrix(self):
|
||||||
return self._projection_matrix
|
return self._projection_matrix
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def vp_matrix(self):
|
||||||
|
return self._projection_matrix * self.cam.view_matrix
|
||||||
|
|
||||||
def resize(self, width, height):
|
def resize(self, width, height):
|
||||||
self.cam.aspect = width / max(height, 1)
|
self.cam.aspect = width / max(height, 1)
|
||||||
self._projection_matrix = self.cam.projection_matrix()
|
self._projection_matrix = self.cam.projection_matrix()
|
||||||
|
self.vp_matrix = None
|
||||||
|
|
||||||
|
def activate_shader(self, name):
|
||||||
|
program = None
|
||||||
|
if self._program != name:
|
||||||
|
if name is None:
|
||||||
|
glUseProgram(0)
|
||||||
|
else:
|
||||||
|
program = self.programs[name]
|
||||||
|
glUseProgram(program.program)
|
||||||
|
self._program = name
|
||||||
|
elif self._program is not None:
|
||||||
|
program = self.programs[self._program]
|
||||||
|
return program
|
||||||
|
|
Loading…
Reference in a new issue