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 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 __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