def points_random_3d(count, range_x=(-10.0, 10.0), range_y=(-10.0, 10.0), range_z=(-10.0, 10.0), seed=None) -> VAO: """ Generates random positions :param count: Number of points :param range_x: min-max range for x axis :param range_y: min-max range for y axis :param range_z: min-max range for z axis :param seed: The random seed to be used """ random.seed(seed) def gen(): for i in range(count): yield random.uniform(*range_x) yield random.uniform(*range_y) yield random.uniform(*range_z) data = numpy.fromiter(gen(), count=count * 3, dtype=numpy.float32) vao = VAO("geometry:points_random_3d", mode=moderngl.POINTS) vao.buffer(data, '3f', ['in_position']) return vao
def init_particles(self): count = 50000 area = 100.0 speed = 5.0 def gen(): for _ in range(count): # Position yield random.uniform(-area, area) yield random.uniform(-area, area) yield random.uniform(-area, area) # Velocity yield random.uniform(-speed, speed) yield random.uniform(-speed, speed) yield random.uniform(-speed, speed) data1 = numpy.fromiter(gen(), count=count * 6, dtype=numpy.float32) data2 = numpy.fromiter(gen(), count=count * 6, dtype=numpy.float32) self.pos1 = self.ctx.buffer(data1.tobytes()) self.particles1 = VAO("particles1", mode=mgl.POINTS) self.particles1.buffer(self.pos1, '3f 3f', ['in_position', 'in_velocity']) self.pos2 = self.ctx.buffer(data2.tobytes()) self.particles2 = VAO("particles2", mode=mgl.POINTS) self.particles2.buffer(self.pos2, '3f 3f', ['in_position', 'in_velocity']) # Set initial start buffers self.particles = self.particles1 self.pos = self.pos2
def load(self, scene, file=None): """Deferred loading""" data = pywavefront.Wavefront(file, create_materials=True) for name, mat in data.materials.items(): if not mat.vertices: continue vbo = numpy.array(mat.vertices, dtype=numpy.float32) vao = VAO(mat.name, mode=moderngl.TRIANGLES) mesh = Mesh(mat.name) # Order: T2F, C3F, N3F and V3F buffer_format = [] attributes = [] if "T2F" in mat.vertex_format: buffer_format.append("2f") attributes.append("in_uv") mesh.add_attribute("TEXCOORD_0", "in_uv", 2) if "C3F" in mat.vertex_format: buffer_format.append("3f") attributes.append("in_color") mesh.add_attribute("NORMAL", "in_color", 3) if "N3F" in mat.vertex_format: buffer_format.append("3f") attributes.append("in_normal") mesh.add_attribute("NORMAL", "in_normal", 3) buffer_format.append("3f") attributes.append("in_position") mesh.add_attribute("POSITION", "in_position", 3) vao.buffer(vbo, " ".join(buffer_format), attributes) mesh.vao = vao scene.meshes.append(mesh) mesh.material = Material(mat.name) mesh.material.color = mat.diffuse if mat.texture: mesh.material.mat_texture = MaterialTexture( texture=textures.get(mat.texture.path, create=True, mipmap=True), sampler=samplers.create( wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE, anisotropy=8, )) node = Node(mesh=mesh) scene.root_nodes.append(node) return scene
def plane_xz(size=(10, 10), resolution=(10, 10)) -> VAO: """ Generates a plane on the xz axis of a specific size and resolution :param size: (x, y) tuple :param resolution: (x, y) tuple :return: VAO """ sx, sz = size rx, rz = resolution dx, dz = sx / rx, sz / rz # step ox, oz = -sx / 2, -sz / 2 # start offset def gen_pos(): for z in range(rz): for x in range(rx): yield ox + x * dx yield 0 yield oz + z * dz def gen_uv(): for z in range(rz): for x in range(rx): yield x / (rx - 1) yield 1 - z / (rz - 1) def gen_normal(): for z in range(rx * rz): yield 0.0 yield 1.0 yield 0.0 def gen_index(): for z in range(rz - 1): for x in range(rx - 1): # quad poly left yield z * rz + x + 1 yield z * rz + x yield z * rz + x + rx # quad poly right yield z * rz + x + 1 yield z * rz + x + rx yield z * rz + x + rx + 1 pos_data = numpy.fromiter(gen_pos(), dtype=numpy.float32) uv_data = numpy.fromiter(gen_uv(), dtype=numpy.float32) normal_data = numpy.fromiter(gen_normal(), dtype=numpy.float32) index_data = numpy.fromiter(gen_index(), dtype=numpy.uint32) vao = VAO("plane_xz", mode=moderngl.TRIANGLES) vao.buffer(pos_data, '3f', ['in_position']) vao.buffer(uv_data, '2f', ['in_uv']) vao.buffer(normal_data, '3f', ['in_normal']) vao.index_buffer(index_data, index_element_size=4) return vao
def points_random_3d(count, range_x=(-10.0, 10.0), range_y=(-10.0, 10.0), range_z=(-10.0, 10.0), seed=None): """ Generates random positions :param count: Number of points :param range_x: min-max range for x axis :param range_y: min-max range for y axis :param range_z: min-max range for z axis :param seed: The random seed to be used """ random.seed(seed) def gen(): for i in range(count): yield random.uniform(*range_x) yield random.uniform(*range_y) yield random.uniform(*range_z) data = numpy.fromiter(gen(), count=count * 3, dtype=numpy.float32) pos = VBO(data) vao = VAO("geometry:points_random_3d", mode=GL.GL_POINTS) vao.add_array_buffer(GL.GL_FLOAT, pos) vao.map_buffer(pos, "in_position", 3) vao.build() return vao
def _post_load(self): """Parse font metadata after resources are loaded""" self._init(Meta(self._config.data)) self._string_buffer = self.ctx.buffer(reserve=self.area[0] * 4 * self.area[1]) self._string_buffer.clear(chunk=b'\32') pos = self.ctx.buffer(data=bytes([0] * 4 * 3)) self._vao = VAO("textwriter", mode=moderngl.POINTS) self._vao.buffer(pos, '3f', 'in_position') self._vao.buffer(self._string_buffer, '1u', 'in_char_id', per_instance=True) self.text_lines = self._text_lines
def load(self, materials): name_map = { 'POSITION': 'in_position', 'NORMAL': 'in_normal', 'TEXCOORD_0': 'in_uv', 'TANGENT': 'in_tangent', 'JOINTS_0': 'in_joints', 'WEIGHTS_0': 'in_heights', 'COLOR_0': 'in_color0', } meshes = [] # Read all primitives as separate meshes for now # According to the spec they can have different materials and vertex format for primitive in self.primitives: vao = VAO(self.name, mode=primitive.mode or moderngl.TRIANGLES) # Index buffer component_type, index_vbo = self.load_indices(primitive) if index_vbo is not None: vao.index_buffer(context.ctx().buffer(index_vbo.tobytes()), index_element_size=component_type.size) attributes = {} vbos = self.prepare_attrib_mapping(primitive) for vbo_info in vbos: dtype, buffer = vbo_info.create() vao.buffer( buffer, " ".join([ "{}{}".format(attr[1], DTYPE_BUFFER_TYPE[dtype]) for attr in vbo_info.attributes ]), [name_map[attr[0]] for attr in vbo_info.attributes], ) for attr in vbo_info.attributes: attributes[attr[0]] = { 'name': name_map[attr[0]], 'components': attr[1], 'type': vbo_info.component_type.value, } bbox_min, bbox_max = self.get_bbox(primitive) meshes.append( Mesh( self.name, vao=vao, attributes=attributes, material=materials[primitive.material] if primitive.material is not None else None, bbox_min=bbox_min, bbox_max=bbox_max, )) return meshes
def _create_vao(self): data = numpy.array([ 0.0, -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 1.0, 1.0, 0.0, -2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 1.0, 1.0, 2.0, -2.0, 0.0, 1.0, 0.0, ], dtype=numpy.float32) vao = VAO("textrenderer", mode=moderngl.TRIANGLES) vao.buffer(data, '3f 2f', ['in_position', 'in_uv']) return vao
def test_create(self): shader = self.create_shader(path="vf_pos.glsl") vao = VAO("test") vao.buffer( self.ctx.buffer(reserve=12), '3f', "in_position", ) vao.draw(shader)
def test_transform(self): shader = self.create_shader(path="v_write_1.glsl") vao = VAO("transform", mode=moderngl.POINTS) vao.buffer( self.ctx.buffer(reserve=12), '1u', 'in_val', ) result = self.ctx.buffer(reserve=12) vao.transform(shader, result) self.assertEqual( result.read(), b'\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00', )
class TextWriter2D(BaseText): def __init__(self, area, text_lines=None, aspect_ratio=1.0): """ :param area: (x, y) Text area size (number of characters) :param size: Text size :param text: Initial text lines """ super().__init__() self.area = area self._text_lines = text_lines self._projection_bytes = None self._aspect_ratio = 1.0 self.aspect_ratio = aspect_ratio self._vao = None self._texture = textures.get('demosys/text/VeraMono.png', cls=TextureArray, layers=190, create=True) self._shader = shaders.get('demosys/text/textwriter2d.glsl', create=True) self._config = data.get('demosys/text/meta.json', create=True) self._string_buffer = None data.on_loaded(self._post_load) def _post_load(self): """Parse font metadata after resources are loaded""" self._init(Meta(self._config.data)) self._string_buffer = self.ctx.buffer(reserve=self.area[0] * 4 * self.area[1]) self._string_buffer.clear(chunk=b'\32') pos = self.ctx.buffer(data=bytes([0] * 4 * 3)) self._vao = VAO("textwriter", mode=moderngl.POINTS) self._vao.buffer(pos, '3f', 'in_position') self._vao.buffer(self._string_buffer, '1u', 'in_char_id', per_instance=True) self.text_lines = self._text_lines @property def text_lines(self): return self._text_lines @text_lines.setter def text_lines(self, value): self._text_lines = value for i, line in enumerate(self._text_lines): self.set_text_line(i, line) @property def aspect_ratio(self): return self._aspect_ratio @aspect_ratio.setter def aspect_ratio(self, value): self._aspect_ratio = value self._projection_bytes = matrix44.create_orthogonal_projection_matrix( -self.aspect_ratio, # left self.aspect_ratio, # right -1.0, # bottom 1.0, # top -100.0, # near 100.0, # far dtype=numpy.float32, ).tobytes() def set_text_line(self, line, text): if line >= self.area[1]: return self._string_buffer.clear(size=self.area[0] * 4, offset=self.area[0] * 4 * line, chunk=b'\32') self._string_buffer.write( numpy.fromiter(self._translate_string(text.encode('iso-8859-1'), self.area[0]), dtype=numpy.uint32).tobytes(), offset=(self.area[0] * 4) * line, ) def draw(self, pos, length=-1, size=1.0): if length < 0: length = self.area[0] * self.area[1] elif length > self.area[0] * self.area[1]: length = self.area[0] * self.area[1] csize = ( self._meta.character_width / self._meta.character_height * size, 1.0 * size, ) cpos = ( pos[0] - self._aspect_ratio + csize[0] / 2, -pos[1] + 1.0 - csize[1] / 2, ) self._texture.use(location=0) self._shader.uniform("m_proj", self._projection_bytes) self._shader.uniform("text_pos", cpos) self._shader.uniform("font_texture", 0) self._shader.uniform("char_size", csize) self._shader.uniform("line_length", self.area[0]) self._vao.draw(self._shader, instances=length)
def bbox(width=1.0, height=1.0, depth=1.0): """ Generates a bounding box. This is simply a box with LINE_STRIP as draw mode :param width: Width of the box :param height: height of the box :param depth: depth of the box :return: VAO """ width, height, depth = width / 2.0, height / 2.0, depth / 2.0 pos = numpy.array([ width, -height, depth, width, height, depth, -width, -height, depth, width, height, depth, -width, height, depth, -width, -height, depth, width, -height, -depth, width, height, -depth, width, -height, depth, width, height, -depth, width, height, depth, width, -height, depth, width, -height, -depth, width, -height, depth, -width, -height, depth, width, -height, -depth, -width, -height, depth, -width, -height, -depth, -width, -height, depth, -width, height, depth, -width, height, -depth, -width, -height, depth, -width, height, -depth, -width, -height, -depth, width, height, -depth, width, -height, -depth, -width, -height, -depth, width, height, -depth, -width, -height, -depth, -width, height, -depth, width, height, -depth, -width, height, -depth, width, height, depth, -width, height, -depth, -width, height, depth, width, height, depth, ], dtype=numpy.float32) vao = VAO("geometry:cube", mode=moderngl.LINE_STRIP) vao.buffer(pos, '3f', ["in_position"]) return vao
def cube(width, height, depth, normals=True, uvs=True): """ Generates a cube centered at 0, 0, 0 :param width: Width of the cube :param height: height of the cube :param depth: depth of the bubs :param normals: (bool) Include normals :param uvs: (bool) include uv coordinates :return: VAO representing the cube """ width, height, depth = width / 2, height / 2, depth / 2 pos = VBO( numpy.array([ width, -height, depth, width, height, depth, -width, -height, depth, width, height, depth, -width, height, depth, -width, -height, depth, width, -height, -depth, width, height, -depth, width, -height, depth, width, height, -depth, width, height, depth, width, -height, depth, width, -height, -depth, width, -height, depth, -width, -height, depth, width, -height, -depth, -width, -height, depth, -width, -height, -depth, -width, -height, depth, -width, height, depth, -width, height, -depth, -width, -height, depth, -width, height, -depth, -width, -height, -depth, width, height, -depth, width, -height, -depth, -width, -height, -depth, width, height, -depth, -width, -height, -depth, -width, height, -depth, width, height, -depth, -width, height, -depth, width, height, depth, -width, height, -depth, -width, height, depth, width, height, depth, ], dtype=numpy.float32)) if normals: normals = VBO( numpy.array([ -0, 0, 1, -0, 0, 1, -0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, ], dtype=numpy.float32)) if uvs: uvs = VBO( numpy.array([ 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0 ], dtype=numpy.float32)) vao = VAO("geometry:cube") # Add buffers vao.add_array_buffer(GL.GL_FLOAT, pos) if normals: vao.add_array_buffer(GL.GL_FLOAT, normals) if uvs: vao.add_array_buffer(GL.GL_FLOAT, uvs) # Map buffers vao.map_buffer(pos, "in_position", 3) if normals: vao.map_buffer(normals, "in_normal", 3) if uvs: vao.map_buffer(uvs, "in_uv", 2) vao.build() return vao
def cube(width, height, depth, center=(0.0, 0.0, 0.0), normals=True, uvs=True) -> VAO: """ Generates a cube VAO. The cube center is (0.0, 0.0 0.0) unless other is specified. :param width: Width of the cube :param height: height of the cube :param depth: depth of the cube :param center: center of the cube :param normals: (bool) Include normals :param uvs: (bool) include uv coordinates :return: VAO representing the cube """ width, height, depth = width / 2.0, height / 2.0, depth / 2.0 pos = numpy.array([ center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] + depth, center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] + width, center[1] - height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] - width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] - width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] + height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, ], dtype=numpy.float32) if normals: normal_data = numpy.array([ -0, 0, 1, -0, 0, 1, -0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, ], dtype=numpy.float32) if uvs: uvs_data = numpy.array([ 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0 ], dtype=numpy.float32) vao = VAO("geometry:cube") # Add buffers vao.buffer(pos, '3f', ['in_position']) if normals: vao.buffer(normal_data, '3f', ['in_normal']) if uvs: vao.buffer(uvs_data, '2f', ['in_uv']) return vao
def plane_xz(size=(10, 10), resolution=(10, 10)): """ Generates a plane on the xz axis of a specific size and resolution :param size: (x, y) tuple :param resolution: (x, y) tuple :return: VAO """ sx, sz = size rx, rz = resolution dx, dz = sx / rx, sz / rz # step ox, oz = -sx / 2, -sz / 2 # start offset def gen_pos(): for z in range(rz): for x in range(rx): yield ox + x * dx yield 0 yield oz + z * dz def gen_uv(): for z in range(rz): for x in range(rx): yield x / (rx - 1) yield 1 - z / (rz - 1) def gen_normal(): for z in range(rx * rz): yield 0.0 yield 1.0 yield 0.0 def gen_index(): for z in range(rz - 1): for x in range(rx - 1): # quad poly left yield z * rz + x yield z * rz + x + 1 yield z * rz + x + rx # quad poly right yield z * rz + x + rx yield z * rz + x + 1 yield z * rz + x + rx + 1 pos_data = numpy.fromiter(gen_pos(), dtype=numpy.float32) position_vbo = VBO(pos_data) uv_data = numpy.fromiter(gen_uv(), dtype=numpy.float32) uv_vbo = VBO(uv_data) normal_data = numpy.fromiter(gen_normal(), dtype=numpy.float32) normal_vbo = VBO(normal_data) index_data = numpy.fromiter(gen_index(), dtype=numpy.uint32) index_vbo = VBO(index_data, target="GL_ELEMENT_ARRAY_BUFFER") vao = VAO("plane_xz", mode=GL.GL_TRIANGLES) vao.add_array_buffer(GL.GL_FLOAT, position_vbo) vao.add_array_buffer(GL.GL_FLOAT, uv_vbo) vao.add_array_buffer(GL.GL_FLOAT, normal_vbo) vao.map_buffer(position_vbo, "in_position", 3) vao.map_buffer(uv_vbo, "in_uv", 2) vao.map_buffer(normal_vbo, "in_normal", 3) vao.set_element_buffer(GL.GL_UNSIGNED_INT, index_vbo) vao.build() return vao
def quad_2d(width, height, xpos=0.0, ypos=0.0) -> VAO: """ Creates a 2D quad VAO using 2 triangles. :param width: Width of the quad :param height: Height of the quad :param xpos: Center position x :param ypos: Center position y """ pos = context.ctx().buffer( numpy.array([ xpos - width / 2.0, ypos + height / 2.0, 0.0, xpos - width / 2.0, ypos - height / 2.0, 0.0, xpos + width / 2.0, ypos - height / 2.0, 0.0, xpos - width / 2.0, ypos + height / 2.0, 0.0, xpos + width / 2.0, ypos - height / 2.0, 0.0, xpos + width / 2.0, ypos + height / 2.0, 0.0, ], dtype=numpy.float32).tobytes()) normals = context.ctx().buffer( numpy.array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, ], dtype=numpy.float32).tobytes()) uvs = context.ctx().buffer( numpy.array([ 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, ], dtype=numpy.float32).tobytes()) vao = VAO("geometry:quad", mode=moderngl.TRIANGLES) vao.buffer(pos, '3f', ["in_position"]) vao.buffer(normals, '3f', ["in_normal"]) vao.buffer(uvs, '2f', ["in_uv"]) return vao
class FeedbackEffect(effect.Effect): def __init__(self): self.feedback = self.get_shader("feedback/transform.glsl") self.shader = self.get_shader("feedback/billboards.glsl") self.texture = self.get_texture("feedback/particle.png") # VAOs representing the two different buffer bindings self.particles1 = None self.particles2 = None self.particles = None # VBOs for each position buffer self.pos1 = None self.pos2 = None self.pos = None self.init_particles() @effect.bind_target def draw(self, time, frametime, target): self.ctx.disable(mgl.DEPTH_TEST) self.ctx.enable(mgl.BLEND) self.ctx.blend_func = mgl.SRC_ALPHA, mgl.ONE_MINUS_SRC_ALPHA m_proj = self.create_projection(90.0, 1.0, 1000.0) # Rotate and translate m_mv = self.create_transformation(rotation=(time * 0.0, time * 0, time * 0), translation=(0.0, 0.0, -40.0)) # Apply the rotation and translation from the system camera m_mv = matrix44.multiply(m_mv, self.sys_camera.view_matrix) gravity_pos = vector3.create(math.sin(time) * 5, math.cos(time) * 5, math.sin(time / 3) * 5) gravity_force = math.cos(time / 2) * 3.0 + 3.0 # gravity_force = 2.0 # Transform positions self.feedback.uniform("gravity_pos", gravity_pos.astype('f4').tobytes()) self.feedback.uniform("gravity_force", gravity_force) self.feedback.uniform("timedelta", frametime) self.particles.transform(self.feedback, self.pos) # Draw particles self.shader.uniform("m_proj", m_proj.astype('f4').tobytes()) self.shader.uniform("m_mv", m_mv.astype('f4').tobytes()) self.texture.use(location=0) self.shader.uniform("texture0", 0) self.particles.draw(self.shader) # Swap buffers self.pos = self.pos1 if self.pos == self.pos2 else self.pos2 self.particles = self.particles1 if self.particles == self.particles2 else self.particles2 def init_particles(self): count = 50000 area = 100.0 speed = 5.0 def gen(): for _ in range(count): # Position yield random.uniform(-area, area) yield random.uniform(-area, area) yield random.uniform(-area, area) # Velocity yield random.uniform(-speed, speed) yield random.uniform(-speed, speed) yield random.uniform(-speed, speed) data1 = numpy.fromiter(gen(), count=count * 6, dtype=numpy.float32) data2 = numpy.fromiter(gen(), count=count * 6, dtype=numpy.float32) self.pos1 = self.ctx.buffer(data1.tobytes()) self.particles1 = VAO("particles1", mode=mgl.POINTS) self.particles1.buffer(self.pos1, '3f 3f', ['in_position', 'in_velocity']) self.pos2 = self.ctx.buffer(data2.tobytes()) self.particles2 = VAO("particles2", mode=mgl.POINTS) self.particles2.buffer(self.pos2, '3f 3f', ['in_position', 'in_velocity']) # Set initial start buffers self.particles = self.particles1 self.pos = self.pos2
def quad_2d(width, height, xpos=0.0, ypos=0.0): """ Creates a 2D quad VAO using 2 triangles. :param width: Width of the quad :param height: Height of the quad :param xpos: Center position x :param ypos: Center position y """ pos = VBO( numpy.array([ xpos - width / 2.0, ypos + height / 2.0, 0.0, xpos - width / 2.0, ypos - height / 2.0, 0.0, xpos + width / 2.0, ypos - height / 2.0, 0.0, xpos - width / 2.0, ypos + height / 2.0, 0.0, xpos + width / 2.0, ypos - height / 2.0, 0.0, xpos + width / 2.0, ypos + height / 2.0, 0.0, ], dtype=numpy.float32)) normals = VBO( numpy.array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, ], dtype=numpy.float32)) uvs = VBO( numpy.array([ 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, ], dtype=numpy.float32)) vao = VAO("geometry:quad", mode=GL.GL_TRIANGLES) vao.add_array_buffer(GL.GL_FLOAT, pos) vao.add_array_buffer(GL.GL_FLOAT, normals) vao.add_array_buffer(GL.GL_FLOAT, uvs) vao.map_buffer(pos, "in_position", 3) vao.map_buffer(normals, "in_normal", 3) vao.map_buffer(uvs, "in_uv", 2) vao.build() return vao
def sphere(radius=0.5, sectors=32, rings=16): """ Generate a sphere :param radius: Radius or the sphere :param rings: number or horizontal rings :param sectors: number of vertical segments :return: VAO containing the sphere """ R = 1.0 / (rings - 1) S = 1.0 / (sectors - 1) vertices = [0] * (rings * sectors * 3) normals = [0] * (rings * sectors * 3) uvs = [0] * (rings * sectors * 2) v, n, t = 0, 0, 0 for r in range(rings): for s in range(sectors): y = math.sin(-math.pi / 2 + math.pi * r * R) x = math.cos(2 * math.pi * s * S) * math.sin(math.pi * r * R) z = math.sin(2 * math.pi * s * S) * math.sin(math.pi * r * R) uvs[t] = s * S uvs[t + 1] = r * R vertices[v] = x * radius vertices[v + 1] = y * radius vertices[v + 2] = z * radius normals[n] = x normals[n + 1] = y normals[n + 2] = z t += 2 v += 3 n += 3 indices = [0] * rings * sectors * 6 i = 0 for r in range(rings - 1): for s in range(sectors - 1): indices[i] = r * sectors + s indices[i + 1] = (r + 1) * sectors + (s + 1) indices[i + 2] = r * sectors + (s + 1) indices[i + 3] = r * sectors + s indices[i + 4] = (r + 1) * sectors + s indices[i + 5] = (r + 1) * sectors + (s + 1) i += 6 vbo_vertices = VBO(numpy.array(vertices, dtype=numpy.float32)) vbo_normals = VBO(numpy.array(normals, dtype=numpy.float32)) vbo_uvs = VBO(numpy.array(uvs, dtype=numpy.float32)) vbo_element = VBO(numpy.array(indices, dtype=numpy.uint32), target="GL_ELEMENT_ARRAY_BUFFER") vao = VAO("sphere") # VBOs vao.add_array_buffer(GL.GL_FLOAT, vbo_vertices) vao.add_array_buffer(GL.GL_FLOAT, vbo_normals) vao.add_array_buffer(GL.GL_FLOAT, vbo_uvs) # Mapping vao.map_buffer(vbo_vertices, "in_position", 3) vao.map_buffer(vbo_normals, "in_normal", 3) vao.map_buffer(vbo_uvs, "in_uv", 2) # Index vao.set_element_buffer(GL.GL_UNSIGNED_INT, vbo_element) vao.build() return vao
def sphere(radius=0.5, sectors=32, rings=16) -> VAO: """ Generate a sphere :param radius: Radius or the sphere :param rings: number or horizontal rings :param sectors: number of vertical segments :return: VAO containing the sphere """ R = 1.0 / (rings - 1) S = 1.0 / (sectors - 1) vertices = [0] * (rings * sectors * 3) normals = [0] * (rings * sectors * 3) uvs = [0] * (rings * sectors * 2) v, n, t = 0, 0, 0 for r in range(rings): for s in range(sectors): y = math.sin(-math.pi / 2 + math.pi * r * R) x = math.cos(2 * math.pi * s * S) * math.sin(math.pi * r * R) z = math.sin(2 * math.pi * s * S) * math.sin(math.pi * r * R) uvs[t] = s * S uvs[t + 1] = r * R vertices[v] = x * radius vertices[v + 1] = y * radius vertices[v + 2] = z * radius normals[n] = x normals[n + 1] = y normals[n + 2] = z t += 2 v += 3 n += 3 indices = [0] * rings * sectors * 6 i = 0 for r in range(rings - 1): for s in range(sectors - 1): indices[i] = r * sectors + s indices[i + 1] = (r + 1) * sectors + (s + 1) indices[i + 2] = r * sectors + (s + 1) indices[i + 3] = r * sectors + s indices[i + 4] = (r + 1) * sectors + s indices[i + 5] = (r + 1) * sectors + (s + 1) i += 6 vbo_vertices = context.ctx().buffer( numpy.array(vertices, dtype=numpy.float32).tobytes()) vbo_normals = context.ctx().buffer( numpy.array(normals, dtype=numpy.float32).tobytes()) vbo_uvs = context.ctx().buffer( numpy.array(uvs, dtype=numpy.float32).tobytes()) vbo_elements = context.ctx().buffer( numpy.array(indices, dtype=numpy.uint32).tobytes()) vao = VAO("sphere", mode=mlg.TRIANGLES) # VBOs vao.buffer(vbo_vertices, '3f', ['in_position']) vao.buffer(vbo_normals, '3f', ['in_normal']) vao.buffer(vbo_uvs, '2f', ['in_uv']) vao.index_buffer(vbo_elements, index_element_size=4) return vao