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
0269de81c0
commit
deee9a818b
|
@ -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
|
||||
from punyverse.glgeom import compile, glRestore, belt, Sphere, 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
|
||||
|
@ -24,11 +24,16 @@ class Entity(object):
|
|||
self.rotation = rotation
|
||||
self.direction = direction
|
||||
|
||||
@cached_property
|
||||
def model_matrix(self):
|
||||
return Matrix4f.from_angles(self.location, self.rotation)
|
||||
|
||||
@cached_property
|
||||
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):
|
||||
self.model_matrix = None
|
||||
self.mv_matrix = None
|
||||
x, y, z = self.location
|
||||
dx, dy, dz = self.direction
|
||||
|
@ -266,15 +271,27 @@ class Body(Entity):
|
|||
|
||||
|
||||
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):
|
||||
super(SphericalBody, self).__init__(name, world, info, parent)
|
||||
|
||||
self.radius = world.evaluate(info.get('radius', world.length)) / world.length
|
||||
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.sphere = Sphere(self.radius, division, division)
|
||||
self.light_source = info.get('light_source', False)
|
||||
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.clouds = None
|
||||
|
@ -306,24 +323,47 @@ class SphericalBody(Body):
|
|||
self.ring_texture = get_best_texture(info['ring'].get('texture', None), clamp=True)
|
||||
self.ring = Disk(distance, distance + size, 30)
|
||||
|
||||
def _draw_sphere(self, fv4=GLfloat * 4):
|
||||
with glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT):
|
||||
glLoadMatrixf(self.mv_matrix.as_gl())
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_BACK)
|
||||
def _draw_planet(self):
|
||||
shader = self.world.activate_shader('planet')
|
||||
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)
|
||||
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
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:
|
||||
glDisable(GL_LIGHTING)
|
||||
else:
|
||||
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)
|
||||
shader.uniform_bool('u_planet.hasNormal', False)
|
||||
shader.uniform_bool('u_planet.hasSpecular', False)
|
||||
shader.uniform_bool('u_planet.hasEmission', False)
|
||||
|
||||
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):
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
def __init__(self, r, lats, longs):
|
||||
tau = pi * 2
|
||||
|
|
|
@ -90,6 +90,15 @@ class Program(object):
|
|||
def uniform_texture(self, 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):
|
||||
variables = {}
|
||||
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,
|
||||
"rotation": 2164320,
|
||||
"light_source": true,
|
||||
"type": "star",
|
||||
"atmosphere": {
|
||||
"diffuse_texture": "sun_diffuse.png",
|
||||
"diffuse_size": 300
|
||||
|
|
|
@ -19,6 +19,7 @@ def load_world(file, callback=lambda message, completion: None):
|
|||
class World(object):
|
||||
PROGRAMS = {
|
||||
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
||||
'planet': ('sphere.vertex.glsl', 'sphere.planet.fragment.glsl'),
|
||||
}
|
||||
|
||||
def __init__(self, file, callback):
|
||||
|
@ -44,6 +45,14 @@ class World(object):
|
|||
for entity in self.tracker:
|
||||
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):
|
||||
programs = {}
|
||||
count = len(self.PROGRAMS)
|
||||
|
|
Loading…
Reference in a new issue