mirror of
https://github.com/quantum5/punyverse.git
synced 2025-04-24 13:11:57 -04:00
Convert planets to use shaders.
This commit is contained in:
parent
66c54a9dbb
commit
fbcdb4d9b0
|
@ -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, SimpleSphere
|
from punyverse.glgeom import compile, glRestore, belt, Sphere, Disk, OrbitVBO, Matrix4f, SimpleSphere, TangentSphere
|
||||||
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
|
||||||
|
@ -24,11 +24,16 @@ class Entity(object):
|
||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def model_matrix(self):
|
||||||
|
return Matrix4f.from_angles(self.location, self.rotation)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def mv_matrix(self):
|
def mv_matrix(self):
|
||||||
return self.world.view_matrix() * Matrix4f.from_angles(self.location, self.rotation)
|
return self.world.view_matrix() * self.model_matrix
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
self.model_matrix = None
|
||||||
self.mv_matrix = None
|
self.mv_matrix = None
|
||||||
x, y, z = self.location
|
x, y, z = self.location
|
||||||
dx, dy, dz = self.direction
|
dx, dy, dz = self.direction
|
||||||
|
@ -266,15 +271,27 @@ class Body(Entity):
|
||||||
|
|
||||||
|
|
||||||
class SphericalBody(Body):
|
class SphericalBody(Body):
|
||||||
|
_sphere_cache = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_sphere(cls, division):
|
||||||
|
if division in cls._sphere_cache:
|
||||||
|
return cls._sphere_cache[division]
|
||||||
|
cls._sphere_cache[division] = sphere = TangentSphere(division, division)
|
||||||
|
return sphere
|
||||||
|
|
||||||
def __init__(self, name, world, info, parent=None):
|
def __init__(self, name, world, info, parent=None):
|
||||||
super(SphericalBody, self).__init__(name, world, info, parent)
|
super(SphericalBody, self).__init__(name, world, info, parent)
|
||||||
|
|
||||||
self.radius = world.evaluate(info.get('radius', world.length)) / world.length
|
self.radius = world.evaluate(info.get('radius', world.length)) / world.length
|
||||||
division = info.get('division', max(min(int(self.radius / 8), 60), 10))
|
division = info.get('division', max(min(int(self.radius / 8), 60), 10))
|
||||||
self.light_source = info.get('light_source', False)
|
|
||||||
|
|
||||||
self.texture = get_best_texture(info['texture'])
|
self.light_source = info.get('light_source', False)
|
||||||
self.sphere = Sphere(self.radius, division, division)
|
self.shininess = info.get('shininess', 0)
|
||||||
|
self.type = info.get('type', 'planet')
|
||||||
|
|
||||||
|
self.diffuse_texture = get_best_texture(info['texture'])
|
||||||
|
self.sphere = self._get_sphere(division)
|
||||||
|
|
||||||
self.atmosphere = None
|
self.atmosphere = None
|
||||||
self.clouds = None
|
self.clouds = None
|
||||||
|
@ -306,24 +323,47 @@ class SphericalBody(Body):
|
||||||
self.ring_texture = get_best_texture(info['ring'].get('texture', None), clamp=True)
|
self.ring_texture = get_best_texture(info['ring'].get('texture', None), clamp=True)
|
||||||
self.ring = Disk(distance, distance + size, 30)
|
self.ring = Disk(distance, distance + size, 30)
|
||||||
|
|
||||||
def _draw_sphere(self, fv4=GLfloat * 4):
|
def _draw_planet(self):
|
||||||
with glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT):
|
shader = self.world.activate_shader('planet')
|
||||||
glLoadMatrixf(self.mv_matrix.as_gl())
|
shader.uniform_float('u_radius', self.radius)
|
||||||
glEnable(GL_CULL_FACE)
|
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||||
glCullFace(GL_BACK)
|
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
|
||||||
|
shader.uniform_mat4('u_mvpMatrix', self.world.vp_matrix * self.model_matrix)
|
||||||
|
|
||||||
glEnable(GL_TEXTURE_2D)
|
glActiveTexture(GL_TEXTURE0)
|
||||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
glBindTexture(GL_TEXTURE_2D, self.diffuse_texture)
|
||||||
|
shader.uniform_bool('u_planet.hasDiffuse', True)
|
||||||
|
shader.uniform_texture('u_planet.diffuseMap', 0)
|
||||||
|
|
||||||
if self.light_source:
|
shader.uniform_bool('u_planet.hasNormal', False)
|
||||||
glDisable(GL_LIGHTING)
|
shader.uniform_bool('u_planet.hasSpecular', False)
|
||||||
else:
|
shader.uniform_bool('u_planet.hasEmission', False)
|
||||||
glDisable(GL_BLEND)
|
|
||||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
|
|
||||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
|
|
||||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
|
|
||||||
|
|
||||||
self.sphere.draw()
|
shader.uniform_vec3('u_planet.ambient', 1, 1, 1)
|
||||||
|
shader.uniform_vec3('u_planet.diffuse', 1, 1, 1)
|
||||||
|
shader.uniform_vec3('u_planet.specular', 0, 0, 0)
|
||||||
|
shader.uniform_vec3('u_planet.emission', 0, 0, 0)
|
||||||
|
|
||||||
|
shader.uniform_float('u_planet.shininess', 0)
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
|
||||||
|
shader.vertex_attribute('a_normal', self.sphere.direction_size, self.sphere.type, GL_FALSE,
|
||||||
|
self.sphere.stride, self.sphere.direction_offset)
|
||||||
|
shader.vertex_attribute('a_tangent', self.sphere.tangent_size, self.sphere.type, GL_FALSE,
|
||||||
|
self.sphere.stride, self.sphere.tangent_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)
|
||||||
|
glActiveTexture(GL_TEXTURE0)
|
||||||
|
|
||||||
|
def _draw_sphere(self):
|
||||||
|
if self.type == 'planet':
|
||||||
|
self._draw_planet()
|
||||||
|
|
||||||
def _draw_atmosphere(self):
|
def _draw_atmosphere(self):
|
||||||
with glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_TEXTURE_BIT):
|
with glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_TEXTURE_BIT):
|
||||||
|
|
|
@ -236,6 +236,45 @@ class SimpleSphere(object):
|
||||||
self.vbo = array_to_gl_buffer(buffer)
|
self.vbo = array_to_gl_buffer(buffer)
|
||||||
|
|
||||||
|
|
||||||
|
class TangentSphere(object):
|
||||||
|
type = GL_FLOAT
|
||||||
|
stride = 7 * 4
|
||||||
|
direction_offset = 0
|
||||||
|
direction_size = 3
|
||||||
|
tangent_offset = direction_size * 4
|
||||||
|
tangent_size = 2
|
||||||
|
uv_offset = tangent_offset + tangent_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 * 8 * [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
|
||||||
|
sphi2, cphi2 = sin(phi2), cos(phi2)
|
||||||
|
sphi1, cphi1 = sin(phi1), cos(phi1)
|
||||||
|
buffer[index:index + 14] = [
|
||||||
|
sine * cphi2, sine * sphi2, dz, sine * -sphi2, sine * cphi2, phi2 / tau, t,
|
||||||
|
sine * cphi1, sine * sphi1, dz, sine * -sphi1, sine * cphi1, phi1 / tau, t,
|
||||||
|
]
|
||||||
|
index += 14
|
||||||
|
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
|
||||||
|
|
|
@ -90,6 +90,15 @@ class Program(object):
|
||||||
def uniform_texture(self, name, index):
|
def uniform_texture(self, name, index):
|
||||||
glUniform1i(self.uniforms[name], index)
|
glUniform1i(self.uniforms[name], index)
|
||||||
|
|
||||||
|
def uniform_float(self, name, value):
|
||||||
|
glUniform1f(self.uniforms[name], value)
|
||||||
|
|
||||||
|
def uniform_bool(self, name, value):
|
||||||
|
glUniform1i(self.uniforms[name], bool(value))
|
||||||
|
|
||||||
|
def uniform_vec3(self, name, a, b, c):
|
||||||
|
glUniform3f(self.uniforms[name], a, b, c)
|
||||||
|
|
||||||
def _variable_locations(self, count_type, get_func, loc_func):
|
def _variable_locations(self, count_type, get_func, loc_func):
|
||||||
variables = {}
|
variables = {}
|
||||||
count = GLint()
|
count = GLint()
|
||||||
|
|
57
punyverse/shaders/sphere.planet.fragment.glsl
Normal file
57
punyverse/shaders/sphere.planet.fragment.glsl
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec2 v_uv;
|
||||||
|
in vec3 v_normal;
|
||||||
|
in vec3 v_position;
|
||||||
|
in vec3 v_camDirection;
|
||||||
|
in mat3 v_TBN;
|
||||||
|
|
||||||
|
struct Surface {
|
||||||
|
bool hasDiffuse;
|
||||||
|
sampler2D diffuseMap;
|
||||||
|
bool hasNormal;
|
||||||
|
sampler2D normalMap;
|
||||||
|
bool hasSpecular;
|
||||||
|
sampler2D specularMap;
|
||||||
|
bool hasEmission;
|
||||||
|
sampler2D emissionMap;
|
||||||
|
vec3 ambient;
|
||||||
|
vec3 diffuse;
|
||||||
|
vec3 specular;
|
||||||
|
vec3 emission;
|
||||||
|
float shininess;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sun {
|
||||||
|
vec3 ambient;
|
||||||
|
vec3 diffuse;
|
||||||
|
vec3 specular;
|
||||||
|
vec3 position;
|
||||||
|
float intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Sun u_sun;
|
||||||
|
uniform Surface u_planet;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 normal = u_planet.hasNormal ? normalize(v_TBN * texture2D(u_planet.normalMap, v_uv).rgb * 2 - 1) : v_normal;
|
||||||
|
vec3 diffuse = u_planet.hasDiffuse ? texture2D(u_planet.diffuseMap, v_uv).rgb : vec3(1);
|
||||||
|
vec3 specular = u_planet.hasSpecular ? texture2D(u_planet.specularMap, v_uv).rgb : vec3(1);
|
||||||
|
vec3 emission = u_planet.hasEmission ? texture2D(u_planet.emissionMap, v_uv).rgb : vec3(1);
|
||||||
|
|
||||||
|
vec3 incident = normalize(u_sun.position - v_position);
|
||||||
|
vec3 reflected = normalize(-reflect(incident, normal));
|
||||||
|
|
||||||
|
float diffuseIntensity = max(dot(normal, incident), 0.0);
|
||||||
|
float shininess = pow(clamp(dot(normalize(v_camDirection), reflected), 0.0, 1.0), u_planet.shininess);
|
||||||
|
|
||||||
|
vec3 ambient = u_planet.ambient * u_sun.ambient * diffuse;
|
||||||
|
diffuse *= u_planet.diffuse * u_sun.diffuse * diffuseIntensity;
|
||||||
|
emission *= u_planet.emission * (1 - min(diffuseIntensity * 2, 1));
|
||||||
|
if (u_planet.shininess > 0)
|
||||||
|
specular *= u_planet.specular * u_sun.specular * clamp(shininess, 0, 1);
|
||||||
|
else
|
||||||
|
specular = vec3(0);
|
||||||
|
|
||||||
|
gl_FragColor = vec4((ambient + diffuse + emission + specular) * u_sun.intensity, 1);
|
||||||
|
}
|
30
punyverse/shaders/sphere.vertex.glsl
Normal file
30
punyverse/shaders/sphere.vertex.glsl
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec3 a_normal;
|
||||||
|
in vec2 a_tangent;
|
||||||
|
in vec2 a_uv;
|
||||||
|
|
||||||
|
out vec2 v_uv;
|
||||||
|
out vec3 v_normal;
|
||||||
|
out vec3 v_position;
|
||||||
|
out vec3 v_camDirection;
|
||||||
|
out mat3 v_TBN;
|
||||||
|
|
||||||
|
uniform float u_radius;
|
||||||
|
uniform mat4 u_mvpMatrix;
|
||||||
|
uniform mat4 u_mvMatrix;
|
||||||
|
uniform mat4 u_modelMatrix;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 position = u_radius * a_normal;
|
||||||
|
|
||||||
|
gl_Position = u_mvpMatrix * vec4(position, 1);
|
||||||
|
|
||||||
|
v_normal = normalize(vec3(u_modelMatrix * vec4(a_normal, 0)));
|
||||||
|
v_uv = a_uv;
|
||||||
|
v_position = (u_modelMatrix * vec4(position, 1)).xyz;
|
||||||
|
v_camDirection = (u_mvMatrix * vec4(position, 1)).xyz;
|
||||||
|
|
||||||
|
vec3 tangent = normalize((u_modelMatrix * vec4(a_tangent, a_normal.z, 0)).xyz);
|
||||||
|
v_TBN = mat3(tangent, cross(tangent, v_normal), v_normal);
|
||||||
|
}
|
|
@ -22,6 +22,7 @@
|
||||||
"mass": 1.9891e+30,
|
"mass": 1.9891e+30,
|
||||||
"rotation": 2164320,
|
"rotation": 2164320,
|
||||||
"light_source": true,
|
"light_source": true,
|
||||||
|
"type": "star",
|
||||||
"atmosphere": {
|
"atmosphere": {
|
||||||
"diffuse_texture": "sun_diffuse.png",
|
"diffuse_texture": "sun_diffuse.png",
|
||||||
"diffuse_size": 300
|
"diffuse_size": 300
|
||||||
|
|
|
@ -19,6 +19,7 @@ def load_world(file, callback=lambda message, completion: None):
|
||||||
class World(object):
|
class World(object):
|
||||||
PROGRAMS = {
|
PROGRAMS = {
|
||||||
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
||||||
|
'planet': ('sphere.vertex.glsl', 'sphere.planet.fragment.glsl'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, file, callback):
|
def __init__(self, file, callback):
|
||||||
|
@ -44,6 +45,14 @@ class World(object):
|
||||||
for entity in self.tracker:
|
for entity in self.tracker:
|
||||||
entity.update()
|
entity.update()
|
||||||
|
|
||||||
|
shader = self.activate_shader('planet')
|
||||||
|
shader.uniform_vec3('u_sun.ambient', 0.1, 0.1, 0.1)
|
||||||
|
shader.uniform_vec3('u_sun.diffuse', 1, 1, 1)
|
||||||
|
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)
|
||||||
|
self.activate_shader(None)
|
||||||
|
|
||||||
def _load_programs(self):
|
def _load_programs(self):
|
||||||
programs = {}
|
programs = {}
|
||||||
count = len(self.PROGRAMS)
|
count = len(self.PROGRAMS)
|
||||||
|
|
Loading…
Reference in a new issue