def render(self): if self.current_pointcloud < len(self.pointclouds): self.handle_mouse() self.handle_keys() self.ctx.viewport = self.wnd.viewport self.ctx.clear(0.0, 0.0, 0.0) self.ctx.enable(mgl.BLEND) vertices = np.load(file=self.pointclouds[self.current_pointcloud]) self.vbo.write(vertices.astype('f4').tobytes()) model = Matrix44.from_scale((self.zoom, self.zoom, self.zoom)) model *= Matrix44.from_x_rotation(-self.theta[1]) model *= Matrix44.from_y_rotation(-self.theta[0]) view = Matrix44.look_at((0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0)) projection = Matrix44.perspective_projection( 45.0, self.wnd.ratio, 0.1, 100.0) self.mvp.write((projection * view * model).astype('f4').tobytes()) self.vao.render(mode=mgl.POINTS) self.sleep_to_target_fps(60) self.current_pointcloud += 1 else: if self.out_dir is None: self.current_pointcloud = 0 else: QtCore.QCoreApplication.instance().quit()
def render(self, time, frame_time): self.ctx.clear(0.2, 0.2, 0.2) self.ctx.enable(moderngl.DEPTH_TEST) self.fps = 1 / frame_time self.control() proj = Matrix44.perspective_projection(45.0, self.aspect_ratio, 0.1, 1000.0) lookat = Matrix44.look_at( (self.movX, self.movY, self.movZ), (200.0, 200.0, 0.0), (0.0, 0.0, 1.0), ) self.light.value = (100, 0, 200) self.texture.use(0) self.mvp_map.write((proj * lookat).astype('f4')) self.vao_map.render(moderngl.TRIANGLE_FAN) model_rot = Matrix44.from_z_rotation( 3.14 / 4) * Matrix44.from_x_rotation(-3.14 / 2) for x in range(int(self.positions.size / 3)): size = 1 + self.production[x] * (2.5 - 1) model_size = Matrix44.from_scale(np.array([size, size, size])) self.gradient.value = self.production[x] model = Matrix44.from_translation(np.array( self.positions[x])) * model_rot * model_size self.mvp.write((proj * lookat * model).astype('f4')) self.vao.render() self.render_ui()
def rotate_relative(self, rotation): pos = self.get_position() rotate_matrix = Matrix44.from_x_rotation(rotation.x) * \ Matrix44.from_y_rotation(rotation.y) * \ Matrix44.from_z_rotation(rotation.z) self.matrix = rotate_matrix * self.matrix self.set_position(pos)
def get_rotation(x, y, z): logger.debug('Create rotation matrix with (x:{}, y:{}, z:{})'.format( x, y, z)) x_rotation = Matrix44.from_x_rotation(x) y_rotation = Matrix44.from_y_rotation(y) z_rotation = Matrix44.from_z_rotation(z) return z_rotation * y_rotation * x_rotation
def buildTransMatrix(pos=[0, 0, 0], rot=[0, 0, 0], scale=[1, 1, 1]): trans = Matrix44.from_translation(pos) rotX = Matrix44.from_x_rotation(np.radians(rot[0])) rotY = Matrix44.from_y_rotation(np.radians(rot[1])) rotZ = Matrix44.from_z_rotation(np.radians(rot[2])) scale = Matrix44.from_scale(scale) tMatrix = trans * scale * rotX * rotY * rotZ return tMatrix
def applyTransforms(vector, projectionMat): r = time.clock() + vector[0] * 10 # rotation offset based on vector's 1st component rotX = Matrix44.from_x_rotation(r) rotY = Matrix44.from_y_rotation(r) rotZ = Matrix44.from_z_rotation(r) trans = Matrix44.from_translation(vector) * Matrix44.from_translation([1,1,-5]) # move view back by -5 scale = Matrix44.from_scale([.5,.5,.5]) # uniformly scale by 0.5 tMatrix = projectionMat * trans * scale * rotX * rotY * rotZ prog['transform'].write(tMatrix.astype('f4').tobytes())
def scatterCubes(vector, projectionMat): view = window.getViewMatrix() r = vector[0] * 10.0 # cube rotation offset based on vector's 1st component rotX = Matrix44.from_x_rotation(r*time.clock()/10.0) # rotate cubes over time rotY = Matrix44.from_y_rotation(r) rotZ = Matrix44.from_z_rotation(r) trans = Matrix44.from_translation(vector) * Matrix44.from_translation([1.0,1.0,-5.0]) scale = Matrix44.from_scale([.5,.5,.5]) # uniformly scale by 0.5 tMatrix = projectionMat * view * trans * scale * rotX * rotY * rotZ prog['transform'].write(tMatrix.astype('f4').tobytes())
def refresh_position(self): center = self.camera.scene.bounding_box.center dummy_cam = Camera() dummy_cam.matrix = self.default_matrix z_shift = dummy_cam.distance_to_point(center) self.camera.matrix = Matrix44.from_translation([self._shift_x, self._shift_y, self._distance]) * \ Matrix44.from_translation([0, 0, -z_shift]) * \ Matrix44.from_x_rotation(-self._angle_y) * \ Matrix44.from_y_rotation(-self._angle_x) * \ Matrix44.from_translation([0, 0, z_shift]) * \ self.default_matrix post_redisplay()
def update(dt): proj = Matrix44.perspective_projection(50, width / height, 0.1, 1000.0) rotX = Matrix44.from_x_rotation(0) rotY = Matrix44.from_y_rotation(time.clock() * 1.5) rotZ = Matrix44.from_z_rotation( 180 * np.pi / 180) # rotate 180 degrees. Convert degrees to radians trans = Matrix44.from_translation( [np.sin(time.clock()) / 4, np.sin(time.clock()) / 4, -1.3]) # bounce diagonally from corner to corner, move back by -1.3 scale = Matrix44.from_scale([.5, .5, .5]) # uniformly scale by 0.5 tMatrix = proj * trans * scale * rotX * rotY * rotZ prog['transform'].write(tMatrix.astype('f4').tobytes()) ctx.clear(.1, .1, .1) vao.render()
def build_matrix(self): """Builds and stores the transformation matrix internally""" m = Matrix44.identity() if isinstance(self.scale, list) or isinstance(self.scale, tuple): m.m11 = self.scale[0] m.m22 = self.scale[1] m.m33 = self.scale[2] else: m *= self.scale m.m44 = 1 m = Matrix44.from_x_rotation(math.radians(self.pitch)) * m m = Matrix44.from_y_rotation(math.radians(self.yaw)) * m m = Matrix44.from_z_rotation(math.radians(self.roll)) * m m = Matrix44.from_translation(Vector3(self.position)) * m self.m = numpy.array(m).astype("f4")
def getModelMatrix(self): scale = mat4.from_scale([0.2, 0.2, 0.2]) roty = mat4.from_y_rotation(-self.hAngle) vdiff = self.vAngle - self.oldvAngle rotx = mat4.from_x_rotation(self.getxRot(vdiff)) zdiff = self.hAngle - self.oldhAngle rotz = mat4.from_z_rotation(-self.getzRot(zdiff)) trans = mat4.from_translation(self.position, dtype='f') self.oldhAngle = self.hAngle self.oldvAngle = self.vAngle return scale * rotz * rotx * roty * trans
def render(self, t): gl.glEnable(gl.GL_DEPTH_TEST) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) M = np.eye(4, dtype=np.float32) M = M * Matrix44.from_scale((.5, .5, .5)) M = M * Matrix44.from_translation((0, 0, -2)) M = M * Matrix44.from_x_rotation(0) M = M * Matrix44.from_scale((.4, .4, .4)) M = M * Matrix44.from_translation((0, 0, -50)) projection = pyrr.matrix44.create_perspective_projection( 3, 1, 0.001, 10000) self.tree.setProjection(projection) self.tree.setModelView(M) self.tree.render() gl.glDisable(gl.GL_DEPTH_TEST)
def build_matrix(self): """Builds and stores the viewport matrix internally Automatically builds the MVP matrix as well""" # Note that by nature, a camera perspective inverts everything # So we negate everything and also do it in reverse # Overrides PositionMatrix, reverse everything, ignore scale m = Matrix44.identity() m = Matrix44.from_translation(-1 * Vector3(self.position)) * m m = Matrix44.from_z_rotation(-math.radians(self.roll)) * m m = Matrix44.from_y_rotation(-math.radians(self.yaw)) * m m = Matrix44.from_x_rotation(-math.radians(self.pitch)) * m if self.tp: # Third person enabled m = Matrix44.from_translation([0, 0, -self.tp_distance]) * m self.m = m self.mvp = numpy.array(self.p * self.m).astype("f4")
def test_m44_q_equivalence(self): """Test for equivalance of matrix and quaternion rotations. Create a matrix and quaternion, rotate each by the same values then convert matrix<->quaternion and check the results are the same. """ m = Matrix44.from_x_rotation(np.pi / 2.) mq = Quaternion.from_matrix(m) q = Quaternion.from_x_rotation(np.pi / 2.) qm = Matrix44.from_quaternion(q) self.assertTrue(np.allclose(np.dot([1., 0., 0., 1.], m), [1., 0., 0., 1.])) self.assertTrue(np.allclose(np.dot([1., 0., 0., 1.], qm), [1., 0., 0., 1.])) self.assertTrue(np.allclose(q * Vector4([1., 0., 0., 1.]), [1., 0., 0., 1.])) self.assertTrue(np.allclose(mq * Vector4([1., 0., 0., 1.]), [1., 0., 0., 1.])) np.testing.assert_almost_equal(np.array(q), np.array(mq), decimal=5) np.testing.assert_almost_equal(np.array(m), np.array(qm), decimal=5)
def frames(): with create_context() as ctx: renderer = Renderer(ctx, (w, h), mesh, projection=projection) for _ in count(): t = time.time() - beginning theta = t * angular_velocity rotation = Matrix44.from_z_rotation( theta[2]) * Matrix44.from_y_rotation( theta[1]) * Matrix44.from_x_rotation(theta[0]) camera = Matrix44.from_translation(np.array([0, 0, -d ])) * rotation renderer.render(camera, light1) buffer = np.mean(renderer.snapshot2(), axis=-1) lines = ascii.shade(buffer) text = "resolution: {w}x{h}, fov: {fov:.2f}, fps: {fps:.2f}, d: {d:.2f}, by: vidstige 2020".format( w=w, h=h, fov=fov, fps=fps, d=d) lines[-2] = scroller(lines[-2], text, t, w=-12) yield b"\033[2J\033[1;1H" + b'\n'.join(lines) + b"\n" duration = (time.time() - beginning) - t if dt - duration > 0: time.sleep(dt - duration)
def render(self, app, currentTime): glBindVertexArray(self._vao.identifier) try: glUseProgram(self._program.identifier) bg_color = ( math.sin(currentTime) * 0.5 + 0.5, math.cos(currentTime) * 0.5 + 0.5, 0.0, 1.0 ) glClearBufferfv(GL_COLOR, 0, bg_color) glClearBufferfv(GL_DEPTH, 0, [1]) f = currentTime * 0.3 mv_matrix = Matrix44.identity(dtype='f4') mv_matrix *= Matrix44.from_x_rotation( currentTime * math.radians(81)) mv_matrix *= Matrix44.from_y_rotation( currentTime * math.radians(45)) mv_matrix *= Matrix44.from_translation([ math.sin(2.1 * f) * 0.5, math.cos(1.7 * f) * 0.5, math.sin(1.3 * f) * math.cos(1.5 * f) * 2.0]) mv_matrix *= Matrix44.from_translation([0.0, 0.0, -4.0]) self._uniform_block.mv_matrix[:] = mv_matrix.reshape(16) glBufferSubData( GL_UNIFORM_BUFFER, 0, ctypes.sizeof(self._uniform_block), ctypes.byref(self._uniform_block)) self._torus_obj.render() finally: glBindVertexArray(NULL_GL_OBJECT)
def render(self, clr_color=(1.0, 1.0, 1.0)): fbo = self.fbo fbo.clear(*clr_color) cam_ratio = self.fbo.size[0] / self.fbo.size[1] p = Matrix44.perspective_projection(self.cam_angle, cam_ratio, 0.00001, 1000000.0) v = Matrix44.look_at( self.cam_pos, (0, 0, 0), self.cam_up, ) rx = Matrix44.from_x_rotation(self.rotate_x) ry = Matrix44.from_y_rotation(self.rotate_y) rz = Matrix44.from_z_rotation(self.rotate_z) m = rx * ry * rz self._projection.write(p.astype('f4').tobytes()) mv = v * m self._modelview.write(mv.astype('f4').tobytes()) n = mv.inverse.transpose() self._normalMat.write(n.astype('f4').tobytes()) self._mode.value = 2 # Phong #lightPos.value = tuple(tuple(n*Vector4.from_vector3(light_pos))[:3]) self._lightPos.value = tuple(self.light_pos) self._lightColor.value = tuple(self.light_color) self._lightPower.value = self.light_power self._ambientColor.value = tuple(self.ambien_color) self._diffuseColor.value = tuple(self.diffuse_color) self._specColor.value = tuple(self.spec_color) self._shininess.value = self.shininess self._mainColor.value = tuple(self.main_color) self.vao.render() return fbo.read()
def test_operators(self): from pyrr import Quaternion, Matrix44, Matrix33, Vector3, Vector4 import numpy as np # matrix multiplication m = Matrix44() * Matrix33() m = Matrix44() * Quaternion() m = Matrix33() * Quaternion() # matrix inverse m = ~Matrix44.from_x_rotation(np.pi) # quaternion multiplication q = Quaternion() * Quaternion() q = Quaternion() * Matrix44() q = Quaternion() * Matrix33() # quaternion inverse (conjugate) q = ~Quaternion() # quaternion dot product d = Quaternion() | Quaternion() # vector oprations v = Vector3() + Vector3() v = Vector4() - Vector4() # vector transform v = Quaternion() * Vector3() v = Matrix44() * Vector3() v = Matrix44() * Vector4() v = Matrix33() * Vector3() # dot and cross products dot = Vector3() | Vector3() cross = Vector3() ^ Vector3()
def test_m44_q_equivalence(self): """Test for equivalance of matrix and quaternion rotations. Create a matrix and quaternion, rotate each by the same values then convert matrix<->quaternion and check the results are the same. """ m = Matrix44.from_x_rotation(np.pi / 2.) mq = Quaternion.from_matrix(m) q = Quaternion.from_x_rotation(np.pi / 2.) qm = Matrix44.from_quaternion(q) self.assertTrue( np.allclose(np.dot([1., 0., 0., 1.], m), [1., 0., 0., 1.])) self.assertTrue( np.allclose(np.dot([1., 0., 0., 1.], qm), [1., 0., 0., 1.])) self.assertTrue( np.allclose(q * Vector4([1., 0., 0., 1.]), [1., 0., 0., 1.])) self.assertTrue( np.allclose(mq * Vector4([1., 0., 0., 1.]), [1., 0., 0., 1.])) np.testing.assert_almost_equal(q, mq, decimal=5) np.testing.assert_almost_equal(m, qm, decimal=5)
def update(self): center = Matrix44.from_translation(self.target) azim = Matrix44.from_z_rotation(self.azimuth) elev = Matrix44.from_x_rotation(self.elevation) dist = Matrix44.from_translation([0, 0, self.distance]) self.mat_lookat = center * azim * elev * dist
def refresh_position(self): self.camera.matrix = Matrix44.from_x_rotation(self._angle_x) * \ Matrix44.from_y_rotation(-self._angle_y) * self.default_matrix post_redisplay()
def render(self, time, frame_time): # Move our camera depending on keybinds self.move_camera() # Black background, turn on depth self.ctx.clear(0.0, 0.0, 0.0) self.ctx.enable(moderngl.DEPTH_TEST) # Set our world scale for tessellation self.scale.write(np.float32(self.camera.scale).astype('f4').tobytes()) # pylint: disable=too-many-function-args # Put projection and look-at matrix into uniform self.mvp.write((self.camera.mat_projection * self.camera.mat_lookat).astype('f4').tobytes()) # Setup time, camera_position into shaders self.time.write(np.float32(time * 0.2).astype('f4').tobytes()) # pylint: disable=too-many-function-args self.camera_position.write( self.camera.camera_position.xy.astype('f4').tobytes()) # Tessellate that floor! self.vao.render(moderngl.PATCHES) # ZEBRA TIME # Put in projection, camera position (which we call light for some reason?), and the time self.zmvp.write((self.camera.mat_projection * self.camera.mat_lookat).astype('f4').tobytes()) self.zlight.write((self.camera.camera_position).astype('f4').tobytes()) self.ztime.write(np.float32(time * 0.2).astype('f4').tobytes()) # pylint: disable=too-many-function-args self.zuse.write(np.float32(self.zebraTime).astype('f4').tobytes()) # pylint: disable=too-many-function-args if not self.zebraTime: # Make the car look forwards properly wheresMyCar = Matrix44.from_translation([0, 0, -0.02]) wheresMyCar = Matrix44.from_x_rotation(np.pi) * wheresMyCar wheresMyCar = Matrix44.from_z_rotation( (np.pi / 2) - self.camera.angle) * wheresMyCar # Put that movement into the shader self.zrotate.write((wheresMyCar).astype('f4').tobytes()) # Set our texture, then render every part of the car with the right color self.car["texture"].use() for i in self.carDict: color = self.carDict[i] self.zcolor.write(np.array(color).astype('f4').tobytes()) self.car[i]["vao"].render() else: # We need to get our zebra looking the right way and also slightly lower than where he starts rotateMyZebra = Matrix44.from_translation([0, -0.05, 0]) rotateMyZebra = Matrix44.from_x_rotation(np.pi / 2) * rotateMyZebra rotateMyZebra = Matrix44.from_z_rotation( (np.pi / 2) - self.camera.angle) * rotateMyZebra # Put that movement into the shader self.zrotate.write((rotateMyZebra).astype('f4').tobytes()) # Show us the zebra! self.texture.use() self.vao2.render()
def rotate_x(self, angle): self._rotation *= Matrix44.from_x_rotation(angle) self._update_uniforms()
def get_rotation_matrix(self): self._module_thetas() return Matrix44.from_x_rotation(self._x_rotate_theta) \ * Matrix44.from_y_rotation(self._y_rotate_theta) \ * Matrix44.from_z_rotation(self._z_rotate_theta)
def rotate_x(self, angle): """Helper function that multiplies the `model_matrix` with a rotation matrix around the x axis.""" m = Matrix44.from_x_rotation(angle) self.model_matrix = m.dot(self.model_matrix)
def main(): glfw.init() glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) window = glfw.create_window(width, height, "LearnOpenGL", None, None) if not window: print("Window Creation failed!") glfw.terminate() glfw.make_context_current(window) glfw.set_window_size_callback(window, on_resize) gl.glEnable(gl.GL_DEPTH_TEST) shader = Shader(CURDIR / 'shaders/6.1.coordinate_systems.vs', CURDIR / 'shaders/6.1.coordinate_systems.fs') vertices = [ # positions tex_coords -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0 ] vertices = (c_float * len(vertices))(*vertices) vao = gl.glGenVertexArrays(1) gl.glBindVertexArray(vao) vbo = gl.glGenBuffers(1) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo) gl.glBufferData(gl.GL_ARRAY_BUFFER, sizeof(vertices), vertices, gl.GL_STATIC_DRAW) gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 5 * sizeof(c_float), c_void_p(0)) gl.glEnableVertexAttribArray(0) gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, gl.GL_FALSE, 5 * sizeof(c_float), c_void_p(3 * sizeof(c_float))) gl.glEnableVertexAttribArray(1) # -- load texture 1 texture1 = gl.glGenTextures(1) gl.glBindTexture(gl.GL_TEXTURE_2D, texture1) # -- texture wrapping gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT) gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT) # -- texture filterting gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) img = Image.open(Tex('container.jpg')).transpose(Image.FLIP_TOP_BOTTOM) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, img.width, img.height, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, img.tobytes()) gl.glGenerateMipmap(gl.GL_TEXTURE_2D) # -- load texture 2 texture2 = gl.glGenTextures(1) gl.glBindTexture(gl.GL_TEXTURE_2D, texture2) # -- texture wrapping gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT) gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT) # -- texture filterting gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) img = Image.open(Tex('awesomeface.png')).transpose(Image.FLIP_TOP_BOTTOM) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, img.width, img.height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, img.tobytes()) gl.glGenerateMipmap(gl.GL_TEXTURE_2D) shader.use() shader.set_int("texture1", 0) shader.set_int("texture2", 1) while not glfw.window_should_close(window): process_input(window) gl.glClearColor(.2, .3, .3, 1.0) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_2D, texture1) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(gl.GL_TEXTURE_2D, texture2) shader.use() model = Matrix44.from_x_rotation( glfw.get_time() * 0.5) * Matrix44.from_y_rotation(glfw.get_time()) view = Matrix44.from_translation([0, 0, -3]) projection = Matrix44.perspective_projection(45, width / height, 0.1, 100.0) shader.set_mat4('view', view) shader.set_mat4('model', model) shader.set_mat4('projection', projection) gl.glBindVertexArray(vao) gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36) glfw.poll_events() glfw.swap_buffers(window) gl.glDeleteVertexArrays(1, id(vao)) gl.glDeleteBuffers(1, id(vbo)) glfw.terminate()