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.aspect = 1
|
||||
self.znear = 1
|
||||
self.zfar = 50000000
|
||||
self.zfar = 3000000
|
||||
|
||||
self.speed = 0
|
||||
self.roll_left = False
|
||||
|
|
|
@ -5,7 +5,7 @@ from pyglet.gl import *
|
|||
# noinspection PyUnresolvedReferences
|
||||
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.orbit import KeplerOrbit
|
||||
from punyverse.texture import get_best_texture, load_clouds
|
||||
|
@ -120,20 +120,30 @@ class Sky(Entity):
|
|||
|
||||
self.texture = get_best_texture(info['texture'])
|
||||
division = info.get('division', 30)
|
||||
self.sphere = Sphere(info.get('radius', 1000000), division, division)
|
||||
self.sphere = SimpleSphere(division, division)
|
||||
|
||||
def draw(self, options):
|
||||
cam = self.world.cam
|
||||
with glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
|
||||
matrix = self.world.view_matrix() * Matrix4f.from_angles((-cam.x, -cam.y, -cam.z), self.rotation)
|
||||
glLoadMatrixf(matrix.as_gl())
|
||||
glEnable(GL_CULL_FACE)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_LIGHTING)
|
||||
shader = self.world.activate_shader('sky')
|
||||
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() *
|
||||
Matrix4f.from_angles(rotation=(cam.pitch, cam.yaw, cam.roll)) *
|
||||
Matrix4f.from_angles(rotation=self.rotation))
|
||||
|
||||
glCullFace(GL_FRONT)
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
self.sphere.draw()
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
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):
|
||||
|
|
|
@ -202,6 +202,40 @@ class Disk(object):
|
|||
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):
|
||||
def __init__(self, r, lats, longs):
|
||||
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": {
|
||||
"texture": "sky.jpg",
|
||||
"rotation": 0,
|
||||
"radius": 305000000,
|
||||
"division": 30,
|
||||
"pitch": 90,
|
||||
"yaw": 30,
|
||||
|
|
|
@ -9,6 +9,7 @@ import six
|
|||
from punyverse import texture
|
||||
from punyverse.camera import Camera
|
||||
from punyverse.entity import *
|
||||
from punyverse.shader import Program
|
||||
|
||||
|
||||
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):
|
||||
PROGRAMS = {
|
||||
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
||||
}
|
||||
|
||||
def __init__(self, file, callback):
|
||||
self.tracker = []
|
||||
self.x = None
|
||||
|
@ -28,6 +33,9 @@ class World(object):
|
|||
|
||||
self.callback = callback
|
||||
self._parse(file)
|
||||
|
||||
self._program = None
|
||||
self.programs = self._load_programs()
|
||||
del self.callback # So it can't be used after loading finishes
|
||||
|
||||
self._time_accumulate = 0
|
||||
|
@ -36,6 +44,15 @@ class World(object):
|
|||
for entity in self.tracker:
|
||||
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):
|
||||
return eval(str(value), {'__builtins__': None}, self._context)
|
||||
|
||||
|
@ -128,6 +145,7 @@ class World(object):
|
|||
def update(self, dt, move, tick):
|
||||
c = self.cam
|
||||
c.update(dt, move)
|
||||
self.vp_matrix = None
|
||||
|
||||
if tick:
|
||||
delta = self.tick_length * dt
|
||||
|
@ -151,6 +169,24 @@ class World(object):
|
|||
def projection_matrix(self):
|
||||
return self._projection_matrix
|
||||
|
||||
@cached_property
|
||||
def vp_matrix(self):
|
||||
return self._projection_matrix * self.cam.view_matrix
|
||||
|
||||
def resize(self, width, height):
|
||||
self.cam.aspect = width / max(height, 1)
|
||||
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