Esempio n. 1
0
class VTKWidget(Widget):
    def __init__(self, **kwargs):
        super(VTKWidget, self).__init__(**kwargs)
        self.setupVTK()

    def updateVTK(self, *largs):
        self.fbo.ask_update()
        self.canvas.ask_update()

    def setupVTK(self):
        Clock.schedule_interval(self.updateVTK, 1 / 1.)
        with self.canvas:
            self.fbo = Fbo(size=(512, 512),
                           clear_color=(.3, .3, .3, .8),
                           push_viewport=True,
                           with_depthbuffer=True)
            self.size = self.fbo.size
            Color(0, 0, 1)
            Rectangle(pos=self.pos, size=self.size, texture=self.fbo.texture)

            Callback(self.drawVTK, reset_context=True)

    def drawVTK(self, instr):
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        VTKClock.tick()
        glViewport(0, 0, 512, 512)
        self.fbo.clear_buffer()

        #push GL state of Kivy
        glUseProgram(0)
        glPushAttrib(GL_ALL_ATTRIB_BITS)
        glMatrixMode(GL_PROJECTION)
        glPushMatrix()
        glMatrixMode(GL_MODELVIEW)
        glPushMatrix()
        glLoadIdentity()

        renWin.Render()

        #pop previous state of Kivy
        glMatrixMode(GL_MODELVIEW)
        glPopMatrix()
        glMatrixMode(GL_PROJECTION)
        glPopMatrix()
        glPopAttrib()
Esempio n. 2
0
class VTKWidget(Widget):
    
    def __init__(self, **kwargs):
        super(VTKWidget,self).__init__(**kwargs)
        self.setupVTK()
    
    def updateVTK(self, *largs):
        self.fbo.ask_update()
        self.canvas.ask_update()

    def setupVTK(self):
        Clock.schedule_interval(self.updateVTK, 1 / 1.)
        with self.canvas:
            self.fbo = Fbo(size=(512,512), clear_color=(.3, .3, .3, .8), push_viewport=True, with_depthbuffer=True)
            self.size = self.fbo.size
            Color(0, 0, 1)
            Rectangle(pos=self.pos, size=self.size, texture=self.fbo.texture)
            
            Callback(self.drawVTK, reset_context=True)
            
    def drawVTK(self, instr):
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        VTKClock.tick()
        glViewport(0,0,512,512)
        self.fbo.clear_buffer()
        
        #push GL state of Kivy
        glUseProgram(0)
        glPushAttrib(GL_ALL_ATTRIB_BITS)
        glMatrixMode(GL_PROJECTION)
        glPushMatrix()
        glMatrixMode(GL_MODELVIEW)
        glPushMatrix()
        glLoadIdentity()
        
        renWin.Render()

        #pop previous state of Kivy
        glMatrixMode(GL_MODELVIEW)
        glPopMatrix()
        glMatrixMode(GL_PROJECTION)
        glPopMatrix()
        glPopAttrib()
Esempio n. 3
0
class Paintbrush(Widget):
    def __init__(self, **kwargs):
        super(Paintbrush, self).__init__(**kwargs)
        self.fbo = Fbo(size=(10, 10))
        self.mesh = Mesh()
        self.points = []
        self.vertices = []
        self.indices = []
        self.line_widths = []
        self.cap_vertices_index = 0
        self.cap_indices_index = 0
        self.mask_lines = []
        self.mask_alphas = []
        self.canvas = RenderContext()
        self.canvas.shader.fs = mask_shader
        self.buffer_container = None
        self.rgb = (0, 1, 1)
        # We'll update our glsl variables in a clock
        Clock.schedule_interval(self.update_glsl, 0)

        # Maintain a window of history for autocorrelations
        self.ac_window = []
        self.ac_position = 0
        self.periodicity_factor = 1.0

    def update_glsl(self, *largs):
        # This is needed for the default vertex shader.
        self.canvas['projection_mat'] = Window.render_context['projection_mat']
        self.canvas['modelview_mat'] = Window.render_context['modelview_mat']

    def on_size(self, instance, value):
        self.canvas.clear()
        with self.canvas:
            self.fbo = Fbo(size=value)
            self.mask_fbo = Fbo(size=(value[0] // 5, value[1] // 5), clear_color=(1, 1, 1, 1))
            Color(*self.rgb, 0.9)
            BindTexture(texture=self.mask_fbo.texture, index=1)
            self.buffer_container = Rectangle(pos=self.pos, size=value, texture=self.fbo.texture)
            #Rectangle(pos=self.pos, size=value, texture=self.mask_fbo.texture)

        self.canvas['texture1'] = 1

        with self.fbo:
            Color(1, 1, 1)
            self.mesh = Mesh(mode='triangle_strip')

    def on_pos(self, instance, value):
        if not self.buffer_container: return
        self.buffer_container.pos = value

    def build_line_segment(self, start, end, future, start_width=8.0, end_width=8.0):
        """Builds a line segment knowing the start and end, as well as one point in the future."""
        start = np.array([start[0], start[1]])
        end = np.array([end[0], end[1]])
        future = np.array([future[0], future[1]])
        length = np.linalg.norm(end - start)
        num_interpolants = max(int(length / DISTANCE_PER_POINT), 3)

        normal = (end - start) / length * start_width / 2.0
        normal = np.array([-normal[1], normal[0]])
        end_normal = (future - end) / max(np.linalg.norm(future - end), 0.1) * end_width / 2.0
        end_normal = np.array([-end_normal[1], end_normal[0]])
        delta_sign = None

        # if (self.last_normal is not None and
        #     np.linalg.norm(normal - self.last_normal) > np.linalg.norm(normal + self.last_normal)):
        #     self.last_normal *= -1

        # Add points deviating in alternating directions around the actual path
        for i in range(num_interpolants):
            path_point = start + (i / num_interpolants) * (end - start)
            delta = normal + (i / num_interpolants) * (end_normal - normal)
            if delta_sign is None:
                delta_sign = 1
                if len(self.points) > 3:
                    second_last_vertex = np.array(self.vertices[-8:-6])
                    option_1 = path_point + delta
                    option_2 = path_point - delta
                    if (np.linalg.norm(option_2 - second_last_vertex) <
                        np.linalg.norm(option_1 - second_last_vertex)):
                        delta_sign *= -1
            self.vertices.extend([*(path_point + delta * delta_sign), 0, 0])
            self.indices.append(len(self.indices))
            delta_sign *= -1

    def add_cap(self, width):
        """Adds a round line cap to the end of the vertex/index list."""
        self.cap_vertices_index = len(self.vertices)
        self.cap_indices_index = len(self.indices)
        if len(self.points) < 3:
            return

        # Extend the current line segment using a circular interpolation of line widths
        start = np.array([self.points[-1][0], self.points[-1][1]])
        prev = np.array([self.points[-2][0], self.points[-2][1]])
        end = start + (start - prev) / max(np.linalg.norm(start - prev), 0.001) * width / 2.0
        length = np.linalg.norm(end - start)
        num_interpolants = max(int(length / DISTANCE_PER_POINT) * 2, 3)

        normal = (end - start) / length * width / 2.0
        normal = np.array([-normal[1], normal[0]])
        end_normal = np.zeros(2)
        delta_sign = None

        # Add points deviating in alternating directions around the actual path
        for i in range(num_interpolants):
            path_point = start + (i / (num_interpolants - 1)) * (end - start)
            circ_progress = 1 - np.sqrt(1 - (i / (num_interpolants - 1)) ** 2)
            delta = normal + circ_progress * (end_normal - normal)
            if delta_sign is None:
                delta_sign = 1
                if len(self.points) > 3:
                    second_last_vertex = np.array(self.vertices[-8:-6])
                    option_1 = path_point + delta
                    option_2 = path_point - delta
                    if (np.linalg.norm(option_2 - second_last_vertex) <
                        np.linalg.norm(option_1 - second_last_vertex)):
                        delta_sign *= -1
            self.vertices.extend([*(path_point + delta * delta_sign), 0, 0])
            self.indices.append(len(self.indices))
            delta_sign *= -1

    def remove_cap(self):
        """Removes a cap on the line."""
        if self.cap_vertices_index > 0 and self.cap_vertices_index <= len(self.vertices):
            del self.vertices[self.cap_vertices_index:]
            del self.indices[self.cap_indices_index:]
        self.cap_vertices_index = 0
        self.cap_indices_index = 0

    def current_line_width(self, depth, window=5):
        """Computes the current line width of the previous `window` points."""
        max_width = 120.0
        min_width = 5.0
        min_dist = 40.0
        max_dist = 140.0
        last_point = self.points[-1]
        old_point = self.points[max(0, len(self.points) - window)]
        if PAINTBRUSH_MODE == 0:
            dist = np.linalg.norm(np.array([last_point[0], last_point[1]]) -
                                  np.array([old_point[0], old_point[1]]))
        else:
            dist = 120.0
        width = (max_dist - dist) * (max_width * 0.8 - min_width) / (max_dist - min_dist)
        if PAINTBRUSH_MODE != 0:
            depth_factor = 1 / (1 + np.exp(-(depth - 0.5) * 4))
            width *= depth_factor
            if PAINTBRUSH_MODE == 2:
                width *= self.periodicity_factor
        return np.clip(width, min_width, max_width)

    def update_periodicity(self, point):
        """Computes a new autocorrelation magnitude by adding the given point."""
        self.ac_window.append(point)
        if len(self.ac_window) > AUTOCORRELATION_WINDOW:
            del self.ac_window[0]
        self.ac_position += 1
        if self.ac_position % 8 == 0 and len(self.ac_window) == AUTOCORRELATION_WINDOW:
            ac_window = np.array(self.ac_window)
            x_fft = np.abs(np.fft.rfft(ac_window[:,0] * np.hanning(AUTOCORRELATION_WINDOW)))
            y_fft = np.abs(np.fft.rfft(ac_window[:,1] * np.hanning(AUTOCORRELATION_WINDOW)))
            x_fft = x_fft[4:20] / np.mean(x_fft[4:20])
            y_fft = y_fft[4:20] / np.mean(y_fft[4:20])
            # if self.ac_position > 200:
            #     plt.figure()
            #     plt.subplot(121)
            #     plt.plot(ac_window[:,0], ac_window[:,1])
            #     plt.subplot(122)
            #     plt.plot(x_fft, label='x')
            #     plt.plot(y_fft, label='y')
            #     plt.show()
            self.periodicity_factor = ((max(1.0, np.max(x_fft) / 4.0) *
                                        max(1.0, np.max(y_fft) / 4.0)) - 1) ** 2 + 1


    def add_point(self, point, depth=None, width=None, alpha=None):
        """
        point: a point in window space to add to the paintbrush trajectory (x, y).
        depth: a 0-1 value indicating the depth into the screen of the current point.
        alpha: a manual 0-1 alpha level for this point.

        Returns the current line width.
        """
        point = (point[0] - self.pos[0], point[1] - self.pos[1])
        self.points.append(point)

        # Build a segment of line
        line_width = 0
        if len(self.points) > 2:
            self.update_periodicity(point)
            line_width = self.current_line_width(depth) if depth is not None else width
            old_line_width = (sum(self.line_widths) / len(self.line_widths)
                              if self.line_widths else line_width)
            self.line_widths.append(line_width)
            if len(self.line_widths) > LINE_WIDTH_WINDOW:
                self.line_widths.pop(0)
            if width is None:
                line_width = sum(self.line_widths) / len(self.line_widths)
            # Clamp the amount by which the line width can change - results in
            # smoother lines
            # line_width = old_line_width + np.clip(line_width - old_line_width, -2.0, 2.0)
            self.remove_cap()
            self.build_line_segment(*self.points[-3:], old_line_width, line_width)
            self.add_cap(line_width)

            # Update mask
            if len(self.points) % MASK_INTERVAL == 0 and len(self.points) > MASK_INTERVAL:
                self.mask_lines.append(Line(points=(self.points[-MASK_INTERVAL - 1][0] / 5,
                                                    self.points[-MASK_INTERVAL - 1][1] /5 ,
                                                    self.points[-1][0] / 5,
                                                    self.points[-1][1] / 5),
                                           width=(line_width + 8.0) / 10))
                if alpha is not None:
                    self.mask_alphas.append(alpha)
                with self.mask_fbo:
                    self.mask_fbo.clear()
                    self.mask_fbo.clear_buffer()
                    if len(self.mask_alphas) == len(self.mask_lines):
                        white_values = self.mask_alphas
                    else:
                        white_values = 1 / (1 + np.exp(-((np.arange(len(self.mask_lines)) -
                                                         len(self.mask_lines)) / FADE_FACTOR + 3)))
                        white_values = white_values * (1 - BASE_FADE) + BASE_FADE
                    for i, (white, line) in enumerate(zip(white_values, self.mask_lines)):
                        Color(white, white, white, 1)
                        self.mask_fbo.add(line)


        # if len(self.points) % 100 == 20:
        #     plt.figure()
        #     plt.plot(self.vertices[::4], self.vertices[1::4])
        #     plt.plot(self.vertices[::4], self.vertices[1::4], 'b.')
        #     plt.plot([x[0] for x in self.points], [x[1] for x in self.points], 'ro')
        #     plt.plot([x[0] for x in self.points], [x[1] for x in self.points], 'r-')
        #     plt.show()

        # self.vertices.extend([point[0], point[1], 0, 0])
        # if len(self.points) > 1:
        #     self.indices.extend([len(self.points) - 2, len(self.points) - 1])
        self.mesh.vertices = self.vertices
        self.mesh.indices = self.indices
        return line_width

    def clear(self):
        self.points = []
        self.vertices = []
        self.indices = []
        self.mesh.vertices = []
        self.mesh.indices = []
        self.periodicity_factor = 1.0
        self.ac_window = []
        self.ac_position = 0
        with self.fbo:
            self.fbo.clear_buffer()
        self.mask_lines = []
        self.mask_colors = []
        with self.mask_fbo:
            self.mask_fbo.clear()
            self.mask_fbo.clear_buffer()
        self.on_size(self, self.size)
Esempio n. 4
0
class PandaView(Widget):
    cam_pos = ListProperty([0, 0, 0])
    cam_lookat = ListProperty([0, 0, 0])

    def __init__(self, **kwargs):
        super(PandaView, self).__init__(**kwargs)
        self.setup_panda()

    def load_model(self, filename):
        return self.msb.load_model(filename)

    def update_panda(self, *largs):
        self.fbo.ask_update()
        self.canvas.ask_update()

    def setup_panda(self, *largs):
        self.msb = ModelShowbase()
        self.msb.camLens.setFov(52.0)
        self.msb.camLens.setNearFar(1.0, 10000.0)

        with self.canvas:
            self.fbo = Fbo(size=self.size, clear_color=(.3, .3, .3, .2))
            Color(1, 1, 1)
            self.viewport = Rectangle(pos=self.pos,
                                      size=self.size,
                                      texture=self.fbo.texture)
            Callback(self.draw_panda, reset_context=True)

        Clock.schedule_interval(self.update_panda, 1 / 60.)

    def draw_panda(self, instr):
        self.fbo.clear_buffer()
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        self.msb.taskMgr.step()

        self.msb.camera.setPos(Point3(*self.cam_pos))
        self.msb.camera.lookAt(Point3(*self.cam_lookat), Vec3(0, 0, 1))

    def toggle_show_collision_boxes(self):
        ''' Debug visualization: Toggle drawing of collision nodes.
            Off by default.
        '''
        # See http://www.panda3d.org/apiref.php?page=NodePath
        matches = self.msb.render.findAllMatches('**/+CollisionNode')
        if self._show_collision_boxes:
            matches.hide()
        else:
            matches.show()
        self._show_collision_boxes = not self._show_collision_boxes
        return self._show_collision_boxes

    def set_show_collision_boxes(self, value):
        if self._show_collision_boxes and value:
            return
        if not self._show_collision_boxes and not value:
            return
        self.toggle_show_collision_boxes()

    def to_panda_window(self, x, y):
        '''
        Convert Kivy window coordinates to panda window coordinates.
        The panda window coordinate system's origin is in the middle of the window,
        with the x-axis going positively to the right and the y axis pointing
        positively up, where coordinates in all directions are normalized to a
        float of maximum value 1. (i.e., top right corner is (1., 1.)).
        '''
        w, h = self.size
        w2 = w / 2.
        h2 = h / 2.
        x = (x - w2) / w2
        y = (y - h2) / h2
        return x, y

    def on_size(self, *args):
        self.fbo.size = self.size
        self.viewport.size = self.size
        props = WindowProperties()
        props.setSize(LVector2i(*self.size))
        self.msb.win.requestProperties(props)
Esempio n. 5
0
class PandaView(Widget):
    cam_pos = ListProperty([0, 0, 0])
    cam_lookat = ListProperty([0, 0, 0])

    def __init__(self, **kwargs):
        super(PandaView, self).__init__(**kwargs)
        self.setup_panda()

    def load_model(self, filename):
        return self.msb.load_model(filename)

    def update_panda(self, *largs):
        self.fbo.ask_update()
        self.canvas.ask_update()

    def setup_panda(self, *largs):
        self.msb = ModelShowbase()
        self.msb.camLens.setFov(52.0)
        self.msb.camLens.setNearFar(1.0, 10000.0)

        with self.canvas:
            self.fbo = Fbo(size=self.size, clear_color=(0.3, 0.3, 0.3, 0.2))
            Color(1, 1, 1)
            self.viewport = Rectangle(pos=self.pos, size=self.size, texture=self.fbo.texture)
            Callback(self.draw_panda, reset_context=True)

        Clock.schedule_interval(self.update_panda, 1 / 60.0)

    def draw_panda(self, instr):
        self.fbo.clear_buffer()
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        self.msb.taskMgr.step()

        self.msb.camera.setPos(Point3(*self.cam_pos))
        self.msb.camera.lookAt(Point3(*self.cam_lookat), Vec3(0, 0, 1))

    def toggle_show_collision_boxes(self):
        """ Debug visualization: Toggle drawing of collision nodes.
            Off by default.
        """
        # See http://www.panda3d.org/apiref.php?page=NodePath
        matches = self.msb.render.findAllMatches("**/+CollisionNode")
        if self._show_collision_boxes:
            matches.hide()
        else:
            matches.show()
        self._show_collision_boxes = not self._show_collision_boxes
        return self._show_collision_boxes

    def set_show_collision_boxes(self, value):
        if self._show_collision_boxes and value:
            return
        if not self._show_collision_boxes and not value:
            return
        self.toggle_show_collision_boxes()

    def to_panda_window(self, x, y):
        """
        Convert Kivy window coordinates to panda window coordinates.
        The panda window coordinate system's origin is in the middle of the window,
        with the x-axis going positively to the right and the y axis pointing
        positively up, where coordinates in all directions are normalized to a
        float of maximum value 1. (i.e., top right corner is (1., 1.)).
        """
        w, h = self.size
        w2 = w / 2.0
        h2 = h / 2.0
        x = (x - w2) / w2
        y = (y - h2) / h2
        return x, y

    def on_size(self, *args):
        self.fbo.size = self.size
        self.viewport.size = self.size
        props = WindowProperties()
        props.setSize(LVector2i(*self.size))
        self.msb.win.requestProperties(props)
Esempio n. 6
0
class EffectWidget(FloatLayout):

    fs = StringProperty(None)

    # Texture of the final Fbo
    texture = ObjectProperty(None)

    # Rectangle clearing Fbo
    fbo_rectangle = ObjectProperty(None)

    # List of effect strings
    effects = ListProperty([])

    # One extra Fbo for each effect
    fbo_list = ListProperty([])

    effect_mask = None
    motion_effect = None

    def __init__(self, **kwargs):
        # Make sure opengl context exists
        EventLoop.ensure_window()
        self.mask_effect = kwargs.get("mask_effect", None)
        self.motion_effect = kwargs.get("motion_effect", None)

        self.canvas = RenderContext(use_parent_projection=True,
                                    use_parent_modelview=True)

        self.size = C_SIZE
        with self.canvas:
            #self._viewport = Rectangle(size=(800,600), pos=self.pos)
            self.fbo = Fbo(size=C_SIZE,
                           with_depthbuffer=True,
                           compute_normal_mat=True,
                           clear_color=(0.3, 0.3, 0.7, 1))

        with self.fbo.before:
            #Rectangle(size=(800, 600))
            PushMatrix()
            self.fbo_translation = Translate(-self.x, -self.y, 0)
            BindTexture(texture=self.mask_effect.texture, index=4)
            BindTexture(texture=self.motion_effect.texture, index=5)

        with self.fbo:
            Color(0, 0, 0)
            BindTexture(texture=self.mask_effect.texture, index=4)
            BindTexture(texture=self.motion_effect.texture, index=5)
            self.fbo_rectangle = Rectangle(size=C_SIZE)
            self._instructions = InstructionGroup()

        with self.fbo.after:
            PopMatrix()
            self.cbs = Callback(self.reset_gl_context)

        super(EffectWidget, self).__init__()
        self.size = C_SIZE

        Clock.schedule_interval(self.update_glsl, 0)

        self._instructions.add(Callback(self.setup_gl_context))

        self.refresh_fbo_setup()
        Clock.schedule_interval(self.update_fbos, 0)

    def on_pos(self, *args):
        self.fbo_translation.x = -self.x
        self.fbo_translation.y = -self.y

    def on_size(self, instance, value):
        self.fbo.size = C_SIZE
        self.fbo_rectangle.size = C_SIZE
        self.refresh_fbo_setup()

        #self._viewport.texture = self.fbo.texture
        #self._viewport.size = value

    def setup_gl_context(self, *args):
        glEnable(GL_DEPTH_TEST)
        self.fbo.clear_buffer()

        #for fbo in self.fbo_list:
        #    fbo.clear_buffer()

    def reset_gl_context(self, *args):
        glDisable(GL_DEPTH_TEST)

    def update_glsl(self, *largs):
        time = Clock.get_boottime()
        resolution = [float(size) for size in C_SIZE]
        self.canvas['time'] = time
        self.canvas['resolution'] = resolution
        self.canvas['texture4'] = 4
        self.canvas['texture5'] = 5
        for fbo in self.fbo_list:
            fbo['time'] = time
            fbo['resolution'] = resolution
            fbo['texture4'] = 4
            fbo['texture5'] = 5

    def on_effects(self, *args):
        self.refresh_fbo_setup()

    def update_fbos(self, *args):
        for fbo in self.fbo_list:
            fbo.ask_update()

    def refresh_fbo_setup(self, *args):
        # Add/remove fbos until there is one per effect
        while len(self.fbo_list) < len(self.effects):
            with self.canvas:
                new_fbo = EffectFbo(size=C_SIZE)
            with new_fbo:
                Color(1, 1, 1, 1)
                new_fbo.texture_rectangle = Rectangle(size=C_SIZE)

                new_fbo.texture_rectangle.size = C_SIZE
            self.fbo_list.append(new_fbo)
        while len(self.fbo_list) > len(self.effects):
            old_fbo = self.fbo_list.pop()
            self.canvas.remove(old_fbo)

        # Do resizing etc.
        self.fbo.size = C_SIZE
        self.fbo_rectangle.size = C_SIZE
        for i in range(len(self.fbo_list)):
            self.fbo_list[i].size = C_SIZE
            self.fbo_list[i].texture_rectangle.size = C_SIZE

        # If there are no effects, just draw our main fbo
        if len(self.fbo_list) == 0:
            self.texture = self.fbo.texture
            return

        for i in range(1, len(self.fbo_list)):
            fbo = self.fbo_list[i]
            fbo.texture_rectangle.texture = self.fbo_list[i - 1].texture

        for effect, fbo in zip(self.effects, self.fbo_list):
            fbo.set_fs(shader_header + shader_uniforms + effect +
                       shader_footer_effect)

        self.fbo_list[0].texture_rectangle.texture = self.fbo.texture
        self.texture = self.fbo_list[-1].texture

    def on_fs(self, instance, value):
        # set the fragment shader to our source code
        shader = self.canvas.shader
        old_value = shader.fs
        shader.fs = value
        if not shader.success:
            shader.fs = old_value
            raise Exception('failed')

    def add_widget(self, widget):
        # Add the widget to our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).add_widget(widget)
        #self._instructions.add(widget.canvas)
        self.canvas = c

    def remove_widget(self, widget):
        # Remove the widget from our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).remove_widget(widget)
        self.canvas = c

    def clear_widgets(self, children=None):
        # Clear widgets from our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).clear_widgets(children)
        self.canvas = c
Esempio n. 7
0
class EffectWidget(FloatLayout):

    fs = StringProperty(None)

    # Texture of the final Fbo
    texture = ObjectProperty(None)

    # Rectangle clearing Fbo
    fbo_rectangle = ObjectProperty(None)

    # List of effect strings
    effects = ListProperty([])

    # One extra Fbo for each effect
    fbo_list = ListProperty([])

    effect_mask = None
    motion_effect = None

    def __init__(self, **kwargs):
        # Make sure opengl context exists
        EventLoop.ensure_window()
        self.mask_effect = kwargs.get("mask_effect", None)
        self.motion_effect = kwargs.get("motion_effect", None)

        self.canvas = RenderContext(use_parent_projection=True,
                                    use_parent_modelview=True)

        self.size = C_SIZE
        with self.canvas:
            #self._viewport = Rectangle(size=(800,600), pos=self.pos)
            self.fbo = Fbo(size=C_SIZE, with_depthbuffer=True,  compute_normal_mat=True,
                           clear_color=(0.3, 0.3, 0.7, 1))

        with self.fbo.before:
            #Rectangle(size=(800, 600))
            PushMatrix()
            self.fbo_translation = Translate(-self.x, -self.y, 0)
            BindTexture(texture=self.mask_effect.texture, index=4)
            BindTexture(texture=self.motion_effect.texture, index=5)

        with self.fbo:
            Color(0, 0, 0)
            BindTexture(texture=self.mask_effect.texture, index=4)
            BindTexture(texture=self.motion_effect.texture, index=5)
            self.fbo_rectangle = Rectangle(size=C_SIZE)
            self._instructions = InstructionGroup()

        with self.fbo.after:
            PopMatrix()
            self.cbs = Callback(self.reset_gl_context)


        super(EffectWidget, self).__init__(**kwargs)
        self.size = C_SIZE

        Clock.schedule_interval(self.update_glsl, 0)

        self._instructions.add(Callback(self.setup_gl_context))

        self.refresh_fbo_setup()
        Clock.schedule_interval(self.update_fbos, 0)

    def on_pos(self, *args):
        self.fbo_translation.x = -self.x
        self.fbo_translation.y = -self.y

    def on_size(self, instance, value):
        self.fbo.size = C_SIZE
        self.fbo_rectangle.size = C_SIZE
        self.refresh_fbo_setup()

        #self._viewport.texture = self.fbo.texture
        #self._viewport.size = value


    def setup_gl_context(self, *args):
        glEnable(GL_DEPTH_TEST)
        self.fbo.clear_buffer()

        #for fbo in self.fbo_list:
        #    fbo.clear_buffer()

    def reset_gl_context(self, *args):
        glDisable(GL_DEPTH_TEST)

    def update_glsl(self, *largs):
        time = Clock.get_boottime()
        resolution = [float(size) for size in C_SIZE]
        self.canvas['time'] = time
        self.canvas['resolution'] = resolution
        self.canvas['texture4'] = 4
        self.canvas['texture5'] = 5
        for fbo in self.fbo_list:
            fbo['time'] = time
            fbo['resolution'] = resolution
            fbo['texture4'] = 4
            fbo['texture5'] = 5


    def on_effects(self, *args):
        self.refresh_fbo_setup()

    def update_fbos(self, *args):
        for fbo in self.fbo_list:
            fbo.ask_update()

    def refresh_fbo_setup(self, *args):
        # Add/remove fbos until there is one per effect
        while len(self.fbo_list) < len(self.effects):
            with self.canvas:
                new_fbo = EffectFbo(size=C_SIZE)
            with new_fbo:
                Color(1, 1, 1, 1)
                new_fbo.texture_rectangle = Rectangle(
                    size=C_SIZE)

                new_fbo.texture_rectangle.size = C_SIZE
            self.fbo_list.append(new_fbo)
        while len(self.fbo_list) > len(self.effects):
            old_fbo = self.fbo_list.pop()
            self.canvas.remove(old_fbo)

        # Do resizing etc.
        self.fbo.size = C_SIZE
        self.fbo_rectangle.size = C_SIZE
        for i in range(len(self.fbo_list)):
            self.fbo_list[i].size = C_SIZE
            self.fbo_list[i].texture_rectangle.size = C_SIZE

        # If there are no effects, just draw our main fbo
        if len(self.fbo_list) == 0:
            self.texture = self.fbo.texture
            return

        for i in range(1, len(self.fbo_list)):
            fbo = self.fbo_list[i]
            fbo.texture_rectangle.texture = self.fbo_list[i - 1].texture

        for effect, fbo in zip(self.effects, self.fbo_list):
            fbo.set_fs(shader_header + shader_uniforms + effect +
                       shader_footer_effect)

        self.fbo_list[0].texture_rectangle.texture = self.fbo.texture
        self.texture = self.fbo_list[-1].texture

    def on_fs(self, instance, value):
        # set the fragment shader to our source code
        shader = self.canvas.shader
        old_value = shader.fs
        shader.fs = value
        if not shader.success:
            shader.fs = old_value
            raise Exception('failed')

    def add_widget(self, widget):
        # Add the widget to our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).add_widget(widget)
        #self._instructions.add(widget.canvas)
        self.canvas = c

    def remove_widget(self, widget):
        # Remove the widget from our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).remove_widget(widget)
        self.canvas = c

    def clear_widgets(self, children=None):
        # Clear widgets from our Fbo instead of the normal canvas
        c = self.canvas
        self.canvas = self.fbo
        super(EffectWidget, self).clear_widgets(children)
        self.canvas = c
Esempio n. 8
0
class PandaView(Widget):
    cam_pos = ListProperty([0, 0, 0])
    cam_lookat = ListProperty([0, 0, 0])

    node = ObjectProperty(None)
    angle = NumericProperty(0)
    radius = NumericProperty(0)
    obj_z = NumericProperty(0)

    sources = ListProperty([])
    nodes = ListProperty([])
    obj_pos = ListProperty([])

    def __init__(self, **kwargs):
        super(PandaView, self).__init__(**kwargs)
        self.setup_panda()
        trigger = Clock.create_trigger(self.update_obj_pos)
        self.bind(
            radius=trigger,
            obj_z=trigger,
        )

    def on_sources(self, instance, values):
        self.node.remove_all_children()
        return self.load_models(values)

    def load_models(self, filenames):
        models = []
        for i, path in enumerate(filenames):
            model = self.msb.load_model(path)
            model.reparent_to(self.node)
            models.append(model)
        self.nodes = models
        return models

    def update_panda(self, *largs):
        self.canvas.ask_update()

    def setup_panda(self, *largs):
        self.msb = msb = ModelShowbase()
        msb.camLens.setFov(52.0)
        msb.camLens.setNearFar(1.0, 10000.0)
        self.node = Node(node=msb.render.attachNewNode('PandaView'))

        with self.canvas:
            self.fbo = Fbo(size=self.size)
            self.viewport = Rectangle(
                pos=self.pos,
                size=self.size,
            )
            Callback(self.draw_panda, reset_context=True)

        Clock.schedule_interval(self.update_panda, 1 / 60.)

    def draw_panda(self, instr):
        self.fbo.clear_buffer()
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        self.msb.taskMgr.step()

        self.msb.camera.setPos(Point3(*self.cam_pos))
        self.msb.camera.lookAt(Point3(*self.cam_lookat), Vec3(0, 0, 1))

    def toggle_show_collision_boxes(self):
        ''' Debug visualization: Toggle drawing of collision nodes.
            Off by default.
        '''
        # See http://www.panda3d.org/apiref.php?page=NodePath
        matches = self.msb.render.findAllMatches('**/+CollisionNode')
        if self._show_collision_boxes:
            matches.hide()
        else:
            matches.show()
        self._show_collision_boxes = not self._show_collision_boxes
        return self._show_collision_boxes

    def set_show_collision_boxes(self, value):
        if self._show_collision_boxes and value:
            return
        if not self._show_collision_boxes and not value:
            return
        self.toggle_show_collision_boxes()

    def to_panda_window(self, x, y):
        '''
        Convert Kivy window coordinates to panda window coordinates.
        The panda window coordinate system's origin is in the middle of the window,
        with the x-axis going positively to the right and the y axis pointing
        positively up, where coordinates in all directions are normalized to a
        float of maximum value 1. (i.e., top right corner is (1., 1.)).
        '''
        w, h = self.size
        w2 = w / 2.
        h2 = h / 2.
        x = (x - w2) / w2
        y = (y - h2) / h2
        return x, y

    def on_size(self, instance, value):
        self.fbo.size = value
        self.viewport.size = value

    def on_pos(self, instance, value):
        self.viewport.pos = value

    def on_obj_pos(self, instance, values):
        for index, pos in enumerate(values):
            self.nodes[index].pos = pos

    def on_angle(self, instance, angle):
        self.node.h = angle * -360. + 180

        for node in self.nodes:
            node.h = angle * -360.

    def update_obj_pos(self, *args):
        self.obj_pos = (
            (
                sin(-2. / 3 * pi) * self.radius,
                cos(-2. / 3 * pi) * self.radius,
                self.obj_z,
            ),
            (
                0,
                self.radius,
                self.obj_z,
            ),
            (
                sin(2. / 3 * pi) * self.radius,
                cos(2. / 3 * pi) * self.radius,
                self.obj_z,
            ),
        )