From f13585676250708425b1fdcc920a226da660ba70 Mon Sep 17 00:00:00 2001 From: Quantum Date: Wed, 29 Aug 2018 00:25:32 -0400 Subject: [PATCH] Use VAOs for everything. --- punyverse/entity.py | 42 +++++++++++++++++--------------- punyverse/model.py | 58 +++++++++++++++++++++++++++------------------ punyverse/shader.py | 26 -------------------- 3 files changed, 58 insertions(+), 68 deletions(-) diff --git a/punyverse/entity.py b/punyverse/entity.py index b04a2d5..fcf5eff 100644 --- a/punyverse/entity.py +++ b/punyverse/entity.py @@ -80,7 +80,8 @@ class AsteroidManager(object): __nonzero__ = __bool__ def load(self, file): - self.asteroids.append(WavefrontVBO(load_model(file), 5, 5, 5)) + shader = self.world.activate_shader('model') + self.asteroids.append(WavefrontVBO(load_model(file), shader, 5, 5, 5)) def new(self, location, direction): return Asteroid(self.world, random.choice(self.asteroids), location, direction) @@ -104,12 +105,27 @@ class Belt(Entity): self.render = gl_info.have_version(3, 3) if self.render: + shader = world.activate_shader('belt') if not isinstance(models, list): models = [models] - self.objects = [WavefrontVBO(load_model(model), info.get('sx', scale), info.get('sy', scale), - info.get('sz', scale)) for model in models] - self.belt = BeltVBO(radius, cross, len(self.objects), count) + self.belt = BeltVBO(radius, cross, len(models), count) + self.objects = [ + WavefrontVBO(load_model(model), shader, info.get('sx', scale), + info.get('sy', scale), info.get('sz', scale)) + for model in models + ] + + def callback(): + glBindBuffer(GL_ARRAY_BUFFER, vbo) + shader.vertex_attribute('a_translate', self.belt.location_size, self.belt.type, GL_FALSE, + self.belt.stride, self.belt.location_offset, divisor=1) + shader.vertex_attribute('a_scale', self.belt.scale_size, self.belt.type, GL_FALSE, + self.belt.stride, self.belt.scale_offset, divisor=1) + glBindBuffer(GL_ARRAY_BUFFER, 0) + + for model, vbo, count in zip(self.objects, self.belt.vbo, self.belt.sizes): + model.additional_attributes(callback) super(Belt, self).__init__(world, name, (x, y, z), (inclination, longitude, argument)) @@ -128,14 +144,7 @@ class Belt(Entity): shader.uniform_mat4('u_modelMatrix', self.model_matrix) for object, vbo, count in zip(self.objects, self.belt.vbo, self.belt.sizes): - glBindBuffer(GL_ARRAY_BUFFER, vbo) - shader.vertex_attribute('a_translate', self.belt.location_size, self.belt.type, GL_FALSE, - self.belt.stride, self.belt.location_offset, divisor=1) - shader.vertex_attribute('a_scale', self.belt.scale_size, self.belt.type, GL_FALSE, - self.belt.stride, self.belt.scale_offset, divisor=1) - glBindBuffer(GL_ARRAY_BUFFER, 0) object.draw(shader, instances=count) - shader.deactivate_all_attributes() class Sky(Entity): @@ -159,7 +168,6 @@ class Sky(Entity): shader.vertex_attribute('a_direction', self.cube.direction_size, self.cube.type, GL_FALSE, self.cube.stride, self.cube.direction_offset) glBindBuffer(GL_ARRAY_BUFFER, 0) - shader.reset_all_attributes() def draw(self, options): cam = self.world.cam @@ -276,7 +284,6 @@ class Body(Entity): glBindBuffer(GL_ARRAY_BUFFER, self.orbit_vbo.vbo) shader.vertex_attribute('a_position', self.orbit_vbo.position_size, self.orbit_vbo.type, GL_FALSE, self.orbit_vbo.stride, self.orbit_vbo.position_offset) - shader.reset_all_attributes() self.orbit_cache = cache return self.orbit_vbo, self.orbit_vao @@ -370,7 +377,6 @@ class SphericalBody(Body): glBindBuffer(GL_ARRAY_BUFFER, 0) else: raise ValueError('Invalid type: %s' % self.type) - shader.reset_all_attributes() self.atmosphere = None self.clouds = None @@ -403,7 +409,6 @@ class SphericalBody(Body): shader.vertex_attribute('a_uv', self.clouds.uv_size, self.clouds.type, GL_FALSE, self.clouds.stride, self.clouds.uv_offset) glBindBuffer(GL_ARRAY_BUFFER, 0) - shader.reset_all_attributes() if atm_texture is not None: self.atm_texture = load_texture_1d(atm_texture, clamp=True) @@ -417,7 +422,6 @@ class SphericalBody(Body): shader.vertex_attribute('a_u', self.atmosphere.u_size, self.atmosphere.type, GL_FALSE, self.atmosphere.stride, self.atmosphere.u_offset) glBindBuffer(GL_ARRAY_BUFFER, 0) - shader.reset_all_attributes() if 'ring' in info: distance = world.evaluate(info['ring'].get('distance', self.radius * 1.2)) @@ -441,7 +445,6 @@ class SphericalBody(Body): shader.vertex_attribute('a_u', self.ring.u_size, self.ring.type, GL_FALSE, self.ring.stride, self.ring.u_offset) glBindBuffer(GL_ARRAY_BUFFER, 0) - shader.reset_all_attributes() def _draw_planet(self): shader = self.world.activate_shader('planet') @@ -584,8 +587,9 @@ class ModelBody(Body): super(ModelBody, self).__init__(name, world, info, parent) scale = info.get('scale', 1) - self.vbo = WavefrontVBO(load_model(info['model']), info.get('sx', scale), info.get('sy', scale), - info.get('sz', scale)) + shader = world.activate_shader('model') + self.vbo = WavefrontVBO(load_model(info['model']), shader, info.get('sx', scale), + info.get('sy', scale), info.get('sz', scale)) def _draw(self, options): shader = self.world.activate_shader('model') diff --git a/punyverse/model.py b/punyverse/model.py index b0bf896..ab57462 100644 --- a/punyverse/model.py +++ b/punyverse/model.py @@ -9,7 +9,7 @@ from pyglet.gl import * # noinspection PyUnresolvedReferences from six.moves import range, zip -from punyverse.glgeom import list_to_gl_buffer +from punyverse.glgeom import list_to_gl_buffer, VAO from punyverse.texture import load_texture @@ -197,36 +197,41 @@ def load_model(path): class ModelVBO(object): - __slots__ = ('has_normal', 'has_texture', 'data_buf', 'index_buf', 'offset_type', 'vertex_count') + __slots__ = ('has_normal', 'has_texture', 'data_buf', 'index_buf', 'offset_type', 'vertex_count', 'vao') - def draw(self, shader, instances=None): + def __init__(self): + self.vao = VAO() + + def build_vao(self, shader): stride = (3 + self.has_normal * 3 + self.has_texture * 2) * 4 - glBindBuffer(GL_ARRAY_BUFFER, self.data_buf) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buf) + with self.vao: + glBindBuffer(GL_ARRAY_BUFFER, self.data_buf) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buf) - shader.vertex_attribute('a_position', 3, GL_FLOAT, GL_FALSE, stride, 0) - if self.has_normal: - shader.vertex_attribute('a_normal', 3, GL_FLOAT, GL_FALSE, stride, 3 * 4) - else: - shader.vertex_attribute_vec3('a_normal', 0, 0, 0) - if self.has_texture: - shader.vertex_attribute('a_uv', 2, GL_FLOAT, GL_FALSE, stride, (6 if self.has_normal else 3) * 4) - else: - shader.vertex_attribute_vec2('a_uv', 0, 0) + shader.vertex_attribute('a_position', 3, GL_FLOAT, GL_FALSE, stride, 0) + if self.has_normal: + shader.vertex_attribute('a_normal', 3, GL_FLOAT, GL_FALSE, stride, 3 * 4) + if self.has_texture: + shader.vertex_attribute('a_uv', 2, GL_FLOAT, GL_FALSE, stride, (6 if self.has_normal else 3) * 4) - if instances: - glDrawElementsInstanced(GL_TRIANGLES, self.vertex_count, self.offset_type, 0, instances) - else: - glDrawElements(GL_TRIANGLES, self.vertex_count, self.offset_type, 0) + glBindBuffer(GL_ARRAY_BUFFER, 0) - shader.deactivate_attributes('a_position', 'a_normal', 'a_uv') - glBindBuffer(GL_ARRAY_BUFFER, 0) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) + def draw(self, shader, instances=None): + with self.vao: + if not self.has_normal: + shader.vertex_attribute_vec3('a_normal', 0, 0, 0) + + if not self.has_texture: + shader.vertex_attribute_vec2('a_uv', 0, 0) + if instances: + glDrawElementsInstanced(GL_TRIANGLES, self.vertex_count, self.offset_type, 0, instances) + else: + glDrawElements(GL_TRIANGLES, self.vertex_count, self.offset_type, 0) class WavefrontVBO(object): - def __init__(self, model, sx=1, sy=1, sz=1): + def __init__(self, model, shader, sx=1, sy=1, sz=1): self._tex_cache = {} self.vbos = [] self.scale = (sx, sy, sz) @@ -240,7 +245,14 @@ class WavefrontVBO(object): normals = model.normals for group in self.merge_groups(model): - self.vbos.append((group.material, self.process_group(group, vertices, normals, textures))) + processed = self.process_group(group, vertices, normals, textures) + self.vbos.append((group.material, processed)) + processed.build_vao(shader) + + def additional_attributes(self, callback): + for _, group in self.vbos: + with group.vao: + callback() def draw(self, shader, instances=None): for mat, vbo in self.vbos: diff --git a/punyverse/shader.py b/punyverse/shader.py index d007348..625efcc 100644 --- a/punyverse/shader.py +++ b/punyverse/shader.py @@ -78,8 +78,6 @@ class Program(object): 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() - self.divisor_attributes = set() def vertex_attribute(self, name, size, type, normalized, stride, offset, divisor=None): location = self.attributes[name] @@ -87,30 +85,6 @@ class Program(object): glEnableVertexAttribArray(location) if divisor: glVertexAttribDivisor(location, divisor) - self.divisor_attributes.add(location) - self.active_attributes.add(location) - - def deactivate_all_attributes(self): - for location in self.active_attributes: - glDisableVertexAttribArray(location) - for location in self.divisor_attributes: - glVertexAttribDivisor(location, 0) - self.reset_all_attributes() - - def deactivate_attributes(self, *attributes): - for attr in attributes: - location = self.attributes[attr] - if location in self.active_attributes: - glDisableVertexAttribArray(location) - if location in self.divisor_attributes: - glVertexAttribDivisor(location, 0) - self.active_attributes.discard(location) - self.divisor_attributes.discard(location) - - def reset_all_attributes(self): - # Call this when you bound to a VAO. - self.active_attributes.clear() - self.divisor_attributes.clear() def vertex_attribute_vec2(self, name, a, b): glVertexAttrib2f(self.attributes[name], a, b)