diff --git a/punyverse/entity.py b/punyverse/entity.py index f7f88aa..7ccc49e 100644 --- a/punyverse/entity.py +++ b/punyverse/entity.py @@ -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, SimpleSphere, TangentSphere +from punyverse.glgeom import compile, glRestore, belt, Disk, OrbitVBO, Matrix4f, SimpleSphere, TangentSphere from punyverse.model import load_model, WavefrontVBO from punyverse.orbit import KeplerOrbit from punyverse.texture import get_best_texture, load_clouds @@ -32,9 +32,14 @@ class Entity(object): def mv_matrix(self): return self.world.view_matrix() * self.model_matrix + @cached_property + def mvp_matrix(self): + return self.world.vp_matrix * self.model_matrix + def update(self): self.model_matrix = None self.mv_matrix = None + self.mvp_matrix = None x, y, z = self.location dx, dy, dz = self.direction self.location = x + dx, y + dy, z + dz @@ -316,8 +321,9 @@ class SphericalBody(Body): atm_texture = atmosphere_data.get('diffuse_texture', None) cloud_texture = atmosphere_data.get('cloud_texture', None) if cloud_texture is not None: - self.cloud_texture = get_best_texture(cloud_texture, loader=load_clouds) - self.clouds = Sphere(self.radius + 2, division, division) + self.cloud_transparency = get_best_texture(cloud_texture, loader=load_clouds) + self.cloud_radius = self.radius + 2 + self.clouds = self._get_sphere(division, tangent=False) if atm_texture is not None: self.atm_texture = get_best_texture(atm_texture, clamp=True) @@ -341,7 +347,7 @@ class SphericalBody(Body): shader.uniform_float('u_radius', self.radius) shader.uniform_mat4('u_modelMatrix', self.model_matrix) shader.uniform_mat4('u_mvMatrix', self.mv_matrix) - shader.uniform_mat4('u_mvpMatrix', self.world.vp_matrix * self.model_matrix) + shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.texture) @@ -395,7 +401,7 @@ class SphericalBody(Body): def _draw_star(self): shader = self.world.activate_shader('star') shader.uniform_float('u_radius', self.radius) - shader.uniform_mat4('u_mvpMatrix', self.world.vp_matrix * self.model_matrix) + shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.texture) @@ -436,17 +442,30 @@ class SphericalBody(Body): self.atmosphere.draw() def _draw_clouds(self): - with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT): - glLoadMatrixf(self.mv_matrix) - glEnable(GL_BLEND) - glEnable(GL_ALPHA_TEST) - glEnable(GL_CULL_FACE) - glDisable(GL_LIGHTING) - glEnable(GL_TEXTURE_2D) + glEnable(GL_BLEND) + shader = self.world.activate_shader('clouds') + shader.uniform_float('u_radius', self.cloud_radius) + shader.uniform_mat4('u_modelMatrix', self.model_matrix) + shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix) - glCullFace(GL_BACK) - glBindTexture(GL_TEXTURE_2D, self.cloud_texture) - self.clouds.draw() + glActiveTexture(GL_TEXTURE0) + glBindTexture(GL_TEXTURE_2D, self.cloud_transparency) + shader.uniform_texture('u_transparency', 0) + shader.uniform_vec3('u_diffuse', 1, 1, 1) + shader.uniform_vec3('u_ambient', 0.1, 0.1, 0.1) + + glBindBuffer(GL_ARRAY_BUFFER, self.clouds.vbo) + shader.vertex_attribute('a_normal', self.clouds.direction_size, self.clouds.type, GL_FALSE, + self.clouds.stride, self.clouds.direction_offset) + shader.vertex_attribute('a_uv', self.clouds.uv_size, self.clouds.type, GL_FALSE, + self.clouds.stride, self.clouds.uv_offset) + + glDrawArrays(GL_TRIANGLE_STRIP, 0, self.clouds.vertex_count) + + shader.deactivate_attributes() + glBindBuffer(GL_ARRAY_BUFFER, 0) + self.world.activate_shader(None) + glDisable(GL_BLEND) def _draw_rings(self): with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT): diff --git a/punyverse/shader.py b/punyverse/shader.py index cfa8dda..0b86a5c 100644 --- a/punyverse/shader.py +++ b/punyverse/shader.py @@ -1,4 +1,5 @@ import os +import sys from ctypes import pointer, byref, create_string_buffer, POINTER, cast from pyglet.gl import * @@ -39,11 +40,15 @@ class Program(object): succeeded = GLint() log_length = GLint() glGetShaderiv(shader, GL_COMPILE_STATUS, byref(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) + 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) + raise CompileError(buffer.value.decode('utf-8')) + elif log_length.value: + print('Warning:', file=sys.stderr) + print(buffer.value.decode('utf-8'), file=sys.stderr) def __init__(self, vertex_file, fragment_file): with glShader(GL_VERTEX_SHADER) as vertex_shader, glShader(GL_FRAGMENT_SHADER) as fragment_shader: diff --git a/punyverse/shaders/clouds.fragment.glsl b/punyverse/shaders/clouds.fragment.glsl new file mode 100644 index 0000000..752b0a5 --- /dev/null +++ b/punyverse/shaders/clouds.fragment.glsl @@ -0,0 +1,19 @@ +#version 130 + +in vec2 v_uv; +in vec3 v_normal; +in vec3 v_position; + +out vec4 o_fragColor; + +uniform vec3 u_ambient; +uniform vec3 u_diffuse; +uniform vec3 u_sun; +uniform sampler2D u_transparency; + +void main() { + vec3 incident = normalize(u_sun - v_position); + vec3 diffuse = u_diffuse * clamp(dot(v_normal, incident) + 0.2, 0.0, 1.0); + + o_fragColor = vec4(u_ambient + diffuse, texture(u_transparency, v_uv).r); +} diff --git a/punyverse/shaders/clouds.vertex.glsl b/punyverse/shaders/clouds.vertex.glsl new file mode 100644 index 0000000..da15ac6 --- /dev/null +++ b/punyverse/shaders/clouds.vertex.glsl @@ -0,0 +1,20 @@ +#version 130 + +in vec3 a_normal; +in vec2 a_uv; + +out vec2 v_uv; +out vec3 v_normal; +out vec3 v_position; + +uniform float u_radius; +uniform mat4 u_mvpMatrix; +uniform mat4 u_modelMatrix; + +void main() { + vec3 position = u_radius * a_normal; + v_uv = a_uv; + v_normal = (u_modelMatrix * vec4(a_normal, 0)).xyz; + v_position = (u_modelMatrix * vec4(position, 1)).xyz; + gl_Position = u_mvpMatrix * vec4(position, 1); +} diff --git a/punyverse/shaders/planet.fragment.glsl b/punyverse/shaders/planet.fragment.glsl index e03b903..aacc639 100644 --- a/punyverse/shaders/planet.fragment.glsl +++ b/punyverse/shaders/planet.fragment.glsl @@ -6,6 +6,8 @@ in vec3 v_position; in vec3 v_camDirection; in mat3 v_TBN; +out vec4 o_fragColor; + struct Surface { sampler2D diffuseMap; bool hasNormal; @@ -49,5 +51,5 @@ void main() { emission *= u_planet.emission * (1 - min(diffuseIntensity * 2, 1)); specular *= u_planet.specular * u_sun.specular * max(shininess, 0) * diffuseIntensity; - gl_FragColor = vec4((ambient + diffuse + emission + specular) * u_sun.intensity, 1); + o_fragColor = vec4((ambient + diffuse + emission + specular) * u_sun.intensity, 1); } diff --git a/punyverse/shaders/sky.fragment.glsl b/punyverse/shaders/sky.fragment.glsl index 304c754..faa0c66 100644 --- a/punyverse/shaders/sky.fragment.glsl +++ b/punyverse/shaders/sky.fragment.glsl @@ -1,8 +1,9 @@ #version 130 in vec2 v_uv; +out vec4 o_fragColor; uniform sampler2D u_skysphere; void main() { - gl_FragColor = vec4(texture(u_skysphere, v_uv).rgb, 1); + o_fragColor = vec4(texture(u_skysphere, v_uv).rgb, 1); } diff --git a/punyverse/shaders/star.fragment.glsl b/punyverse/shaders/star.fragment.glsl index 899f94d..2d9db21 100644 --- a/punyverse/shaders/star.fragment.glsl +++ b/punyverse/shaders/star.fragment.glsl @@ -1,8 +1,9 @@ #version 130 in vec2 v_uv; +out vec4 o_fragColor; uniform sampler2D u_emission; void main() { - gl_FragColor = vec4(texture(u_emission, v_uv).rgb, 1); + o_fragColor = vec4(texture(u_emission, v_uv).rgb, 1); } diff --git a/punyverse/texture.py b/punyverse/texture.py index b1d9dd4..c58ab9a 100644 --- a/punyverse/texture.py +++ b/punyverse/texture.py @@ -1,6 +1,5 @@ from __future__ import print_function -import itertools import os.path import struct from ctypes import c_int, byref, c_uint @@ -9,7 +8,7 @@ from io import BytesIO import six from pyglet import image from pyglet.gl import * -from six.moves import zip, range +from six.moves import range try: from ._glgeom import bgr_to_rgb, flip_vertical @@ -229,21 +228,26 @@ def load_clouds(file): path, width, height, depth, mode, texture = load_image(file, path) + if depth != 1: + texture = texture[::depth] + buffer = c_uint() glGenTextures(1, byref(buffer)) id = buffer.value - pixels = bytearray(len(texture) * 4) - white = b'\xff'[0] - pixels[:] = itertools.chain.from_iterable(zip(itertools.repeat(white), itertools.repeat(white), - itertools.repeat(white), - itertools.islice(texture, 0, None, depth))) - glBindTexture(GL_TEXTURE_2D, id) + if gl_info.have_version(3) or gl_info.have_extension('GL_ARB_framebuffer_object'): + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, texture) + glGenerateMipmap(GL_TEXTURE_2D) + else: + gluBuild2DMipmaps(GL_TEXTURE_2D, 1, width, height, GL_RED, GL_UNSIGNED_BYTE, texture) + 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, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, six.binary_type(pixels)) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) + + 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)) cache[path] = id return id diff --git a/punyverse/world.py b/punyverse/world.py index 927e913..7ab3464 100644 --- a/punyverse/world.py +++ b/punyverse/world.py @@ -20,6 +20,7 @@ class World(object): PROGRAMS = { 'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'), 'planet': ('planet.vertex.glsl', 'planet.fragment.glsl'), + 'clouds': ('clouds.vertex.glsl', 'clouds.fragment.glsl'), 'star': ('star.vertex.glsl', 'star.fragment.glsl'), } @@ -52,6 +53,9 @@ class World(object): shader.uniform_vec3('u_sun.specular', 0.5, 0.5, 0.5) shader.uniform_vec3('u_sun.position', 0, 0, 0) shader.uniform_float('u_sun.intensity', 1) + + shader = self.activate_shader('clouds') + shader.uniform_vec3('u_sun', 0, 0, 0) self.activate_shader(None) def _load_programs(self):