def _make_cursor_view_matrix(self, x, y): "Calculate a view matrix for placing the custom cursor on screen." ww, wh = self.get_size() iw, ih = self.mouse_texture.size scale = 1 width = ww / iw / scale height = wh / ih / scale far = 10 near = -10 frust = Matrix4() frust[:] = (2 / width, 0, 0, 0, 0, 2 / height, 0, 0, 0, 0, -2 / (far - near), 0, 0, 0, -(far + near) / (far - near), 1) x -= ww / 2 y -= wh / 2 lx = x / iw / scale ly = y / ih / scale view = Matrix4().new_translate(lx, ly, 0) return frust * view
def generate_transform_matrix(self): m_rotate_base = Matrix4.new_look_at( Point3(0, 0, 0), -self.origin.direction, self.origin.up).inverse() m = Matrix4.new_look_at( Point3(0, 0, 0), -self.position.direction, self.position.up) * m_rotate_base move = self.position.position - self.origin.position m.d, m.h, m.l = move.x, move.y, move.z return m
def on_draw(): a = 1.6 b = a * sqrt(3) / 3 r = sqrt(1 - b * b) R = Vector3(a, a, a).normalized() * b cs = Matrix4.new_translate(R[0], R[1], R[2]) * Matrix4.new_look_at( Vector3(0, 0, 0), R, Vector3(0, 1, 0)) glDisable(GL_DEPTH_TEST) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() draw_coords(Matrix4.new_identity(), 2) draw_line((0, 0, 0), R, (1, 1, 0)) draw_coords(cs, r, 3) glEnable(GL_DEPTH_TEST) draw_triangle((a, 0, 0), (0, a, 0), (0, 0, a), (.5, .5, 1), .2) draw_sphere(3, (1, .5, .5), .2)
def make_view_matrix(window_size, image_size, zoom, offset): "Calculate a view matrix that places the image on the screen, at scale." ww, wh = window_size iw, ih = image_size scale = 2**zoom width = ww / iw / scale height = wh / ih / scale far = 10 near = -10 frust = Matrix4() frust[:] = (2 / width, 0, 0, 0, 0, 2 / height, 0, 0, 0, 0, -2 / (far - near), 0, 0, 0, -(far + near) / (far - near), 1) x, y = offset lx = x / iw / scale ly = y / ih / scale view = (Matrix4().new_translate(lx, ly, 0)) return frust * view
def _orig_euclid_look_at(eye, at, up): ''' Taken from the original source of PyEuclid's Matrix4.new_look_at() prior to 1184a07d119a62fc40b2c6becdbeaf053a699047 (11 Jan 2015), as discussed here: https://github.com/ezag/pyeuclid/commit/1184a07d119a62fc40b2c6becdbeaf053a699047 We were dependent on the old behavior, which is duplicated here: ''' z = (eye - at).normalized() x = up.cross(z).normalized() y = z.cross(x) m = Matrix4.new_rotate_triple_axis(x, y, z) m.d, m.h, m.l = eye.x, eye.y, eye.z return m
def on_draw(self): # Prevent trying to draw before things have been set up if not hasattr(self, "offscreen_buffer"): return # Model matrix we'll use to position the main model suzanne_model_matrix = (Matrix4.new_identity().rotatex( -math.pi / 2).rotatez(time())) # Rotate over time plane_model_matrix = Matrix4.new_rotatey(math.pi).translate(0, 0, 2) # Render to an offscreen buffer with self.offscreen_buffer, self.view_program, \ enabled(gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE): gl.glDepthMask(gl.GL_TRUE) w, h = self.size aspect = h / w # Calculate a view frustum; this is basically our camera. near = 5 far = 15 width = 2 height = 2 * aspect frustum = (Matrix4.new(near / width, 0, 0, 0, 0, near / height, 0, 0, 0, 0, -(far + near) / (far - near), -1, 0, 0, -2 * far * near / (far - near), 0)) # The view matrix positions the camera in the scene view_matrix = (Matrix4.new_identity().translate(0, 0, -8)) # Send the matrices to GL gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, gl_matrix(frustum * view_matrix)) gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE, gl_matrix(suzanne_model_matrix)) gl.glUniform4f(2, 0.3, 0.3, 1, 1) # Set the "color" uniform to blue self.suzanne.draw() # We'll also draw a simple plane behind the main model gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE, gl_matrix(plane_model_matrix)) gl.glUniform4f(2, 0.3, 1, 0.3, 1) # Set the "color" uniform to green self.plane.draw(mode=gl.GL_TRIANGLE_STRIP) # Render shadow buffer # Basically the same scene as above, but to a different buffer and from a different view with self.shadow_buffer, self.view_program, enabled( gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE): gl.glDepthMask(gl.GL_TRUE) frustum = Matrix4.new_perspective(1, 1, 1, 12) view_matrix = (Matrix4.new_identity().translate( 0, 0, -4).rotatey(0.5).rotatex(0.3)) light_pos = (view_matrix.inverse() * Point3(0, 0, 0)) light_view_matrix = frustum * view_matrix gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, gl_matrix(light_view_matrix)) gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE, gl_matrix(suzanne_model_matrix)) gl.glUniform4f(2, 0.9, 0.3, 0.4, 1) self.suzanne.draw() gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE, gl_matrix(plane_model_matrix)) self.plane.draw(mode=gl.GL_TRIANGLE_STRIP) # Now draw the offscreen buffer to another buffer, combining it with the # lighting information to get a nice image. # Note: This step is pretty pointless here, as we might just draw directly to screen. # Just demonstrates how to do it. with self.vao, self.offscreen_buffer2, self.lighting_program, disabled( gl.GL_CULL_FACE, gl.GL_DEPTH_TEST): gl.glUniform3f(0, *light_pos) gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE, gl_matrix(light_view_matrix)) # Bind some of the offscreen buffer's textures so the shader can read them. with self.offscreen_buffer["color"], self.offscreen_buffer["normal"], \ self.offscreen_buffer["position"], self.shadow_buffer["depth"]: gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6) # Now render the finished image to the screen with self.vao, self.copy_program, disabled(gl.GL_CULL_FACE, gl.GL_DEPTH_TEST): with self.offscreen_buffer2["color"]: gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
def __call__(self, oldpaint, imgui, drawing, brush, altitude: float=-120, azimuth: float=0, spin: bool=False): selection = drawing.selection if selection: size = selection.size depth = len(drawing.layers) colors = drawing.palette.as_tuple() mesh = self._get_mesh(tuple(drawing.layers), selection, colors) if not mesh: # TODO hacky self.texture and self.texture[0].clear() return w, h = size model_matrix = Matrix4.new_translate(-w/2, -h/2, depth/2).scale(1, 1, 1/math.sin(math.pi/3)) far = w*2 near = 0 frust = Matrix4() frust[:] = (2/w, 0, 0, 0, 0, 2/h, 0, 0, 0, 0, -2/(far-near), 0, 0, 0, -(far+near)/(far-near), 1) offscreen_buffer = self._get_buffer(size) with offscreen_buffer, self.program, \ enabled(gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE): azimuth = math.degrees(time()) if spin else azimuth view_matrix = ( Matrix4 # .new_scale(2/w, 2/h, 1/max(w, h)) .new_translate(0, 0, -w) .rotatex(math.radians(altitude)) .rotatez(math.radians(azimuth)) # Rotate over time ) gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, gl_matrix(frust * view_matrix * model_matrix)) gl.glViewport(0, 0, *size) gl.glPointSize(1.0) mesh.draw(mode=gl.GL_POINTS) shadow_buffer = self._get_shadow_buffer(size) with shadow_buffer, self.program, \ enabled(gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE): view_matrix = ( Matrix4 # .new_scale(2/w, 2/h, 1/max(w, h)) .new_translate(0, 0, -5) .rotatex(math.pi) .rotatez(azimuth) # Rotate over time ) gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, gl_matrix(frust * view_matrix * model_matrix)) gl.glViewport(0, 0, *size) gl.glPointSize(1.0) mesh.draw(mode=gl.GL_POINTS) final_buffer = self._get_final_buffer(size) with self._vao, final_buffer, self._copy_program, disabled(gl.GL_CULL_FACE, gl.GL_DEPTH_TEST): with offscreen_buffer["color"], offscreen_buffer["normal"], offscreen_buffer["position"], shadow_buffer["depth"]: gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6) # TODO must be careful here so that the texture is always valid # (since imgui may read it at any time) Find a way to ensure this. texture = self._get_texture(size) gl.glCopyImageSubData(final_buffer["color"].name, gl.GL_TEXTURE_2D, 0, 0, 0, 0, texture.name, gl.GL_TEXTURE_2D, 0, 0, 0, 0, w, h, 1) self.texture = texture, size
def __call__(self, voxpaint, drawing, altitude: float = 120, azimuth: float = 45, spin: bool = False): size = drawing.size depth = drawing.shape[2] colors = drawing.palette.colors x = math.sin(math.pi / 3) altitude = math.radians(altitude) azimuth = math.radians(azimuth) mesh = self._get_mesh(drawing, drawing.version, drawing.hidden_layers_by_axis) if not mesh: # TODO hacky self.texture and self.texture[0].clear() return w, h = size vw = int(w * math.sqrt(2)) vh = int(h + math.sqrt(2) * h // 2) view_size = (vw, vh) model_matrix = (Matrix4.new_scale(1, 1, 1 / x).translate( -w // 2, -h // 2, depth // 2 - 1 / 2)) far = w * 2 near = -w * 2 frust = Matrix4() frust[:] = (2 / vw, 0, 0, 0, 0, 2 / vh, 0, 0, 0, 0, -2 / (far - near), 0, 0, 0, -(far + near) / (far - near), 1) offscreen_buffer = self._get_buffer(view_size) with offscreen_buffer, self.program, \ enabled(gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE): azimuth = time() if spin else azimuth view_matrix = ( Matrix4.new_translate(0, 0, -1).rotatex(altitude).rotatez( azimuth) # Rotate over time ) colors = self._get_colors(colors) gl.glUniform4fv(3, 256, colors) gl.glUniformMatrix4fv( 0, 1, gl.GL_FALSE, gl_matrix(frust * view_matrix * model_matrix)) gl.glViewport(0, 0, vw, vh) gl.glPointSize(1) mesh.draw(mode=gl.GL_POINTS) final_buffer = self._get_final_buffer(view_size) with self._vao, final_buffer, self._copy_program, disabled( gl.GL_CULL_FACE, gl.GL_DEPTH_TEST): with offscreen_buffer["color"], offscreen_buffer[ "normal"], offscreen_buffer["position"]: gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6) # TODO must be careful here so that the texture is always valid # (since imgui may read it at any time) Find a way to ensure this. texture = self._get_texture(view_size) gl.glCopyImageSubData(final_buffer["color"].name, gl.GL_TEXTURE_2D, 0, 0, 0, 0, texture.name, gl.GL_TEXTURE_2D, 0, 0, 0, 0, vw, vh, 1) self.texture = texture, view_size