diff --git a/p5/sketch/openglrenderer.py b/p5/sketch/openglrenderer.py new file mode 100644 index 00000000..63c3f158 --- /dev/null +++ b/p5/sketch/openglrenderer.py @@ -0,0 +1,139 @@ +from abc import ABC +import numpy as np + +from vispy.gloo import Program, VertexBuffer, FrameBuffer, IndexBuffer +from .shaders2d import src_default, src_fbuffer + + +# Abstract class that contains common code for OpenGL renderers +class OpenGLRenderer(ABC): + def __init__(self): + self.default_prog = None + self.fbuffer_prog = None + + self.fbuffer = None + self.fbuffer_tex_front = None + self.fbuffer_tex_back = None + + self.vertex_buffer = None + self.index_buffer = None + + # Renderer Globals: USEFUL CONSTANTS + self.COLOR_WHITE = (1, 1, 1, 1) + self.COLOR_BLACK = (0, 0, 0, 1) + self.COLOR_DEFAULT_BG = (0.8, 0.8, 0.8, 1.0) + + # Renderer Globals: STYLE/MATERIAL PROPERTIES + # + self.background_color = self.COLOR_DEFAULT_BG + + self.fill_color = self.COLOR_WHITE + self.fill_enabled = True + + self.stroke_color = self.COLOR_BLACK + self.stroke_enabled = True + + self.tint_color = self.COLOR_BLACK + self.tint_enabled = False + + # Renderer Globals: Curves + self.stroke_weight = 1 + self.stroke_cap = 2 + self.stroke_join = 0 + + # Renderer Globals + # VIEW MATRICES, ETC + # + self.viewport = None + self.texture_viewport = None + self.transform_matrix = np.identity(4) + self.projection_matrix = np.identity(4) + + # Renderer Globals: RENDERING + self.draw_queue = [] + + def initialize_renderer(self): + self.fbuffer = FrameBuffer() + + vertices = np.array([[-1.0, -1.0], + [+1.0, -1.0], + [-1.0, +1.0], + [+1.0, +1.0]], + np.float32) + texcoords = np.array([[0.0, 0.0], + [1.0, 0.0], + [0.0, 1.0], + [1.0, 1.0]], + dtype=np.float32) + + self.fbuf_vertices = VertexBuffer(data=vertices) + self.fbuf_texcoords = VertexBuffer(data=texcoords) + + self.fbuffer_prog = Program(src_fbuffer.vert, src_fbuffer.frag) + self.fbuffer_prog['texcoord'] = self.fbuf_texcoords + self.fbuffer_prog['position'] = self.fbuf_vertices + + self.vertex_buffer = VertexBuffer() + self.index_buffer = IndexBuffer() + + self.default_prog = Program(src_default.vert, src_default.frag) + + def render_default(self, draw_type, draw_queue): + # 1. Get the maximum number of vertices persent in the shapes + # in the draw queue. + # + if len(draw_queue) == 0: + return + + num_vertices = 0 + for vertices, _, _ in draw_queue: + num_vertices = num_vertices + len(vertices) + + # 2. Create empty buffers based on the number of vertices. + # + data = np.zeros(num_vertices, + dtype=[('position', np.float32, 3), + ('color', np.float32, 4)]) + + # 3. Loop through all the shapes in the geometry queue adding + # it's information to the buffer. + # + sidx = 0 + draw_indices = [] + for vertices, idx, color in draw_queue: + num_shape_verts = len(vertices) + + data['position'][sidx:(sidx + num_shape_verts), ] = np.array(vertices) + + color_array = np.array([color] * num_shape_verts) + data['color'][sidx:sidx + num_shape_verts, :] = color_array + + draw_indices.append(sidx + idx) + + sidx += num_shape_verts + + self.vertex_buffer.set_data(data) + self.index_buffer.set_data(np.hstack(draw_indices)) + + # 4. Bind the buffer to the shader. + # + self.default_prog.bind(self.vertex_buffer) + + # 5. Draw the shape using the proper shape type and get rid of + # the buffers. + # + self.default_prog.draw(draw_type, indices=self.index_buffer) + + def cleanup(self): + """Run the clean-up routine for the renderer. + + This method is called when all drawing has been completed and the + program is about to exit. + + """ + self.default_prog.delete() + self.fbuffer_prog.delete() + self.fbuffer.delete() + + def _transform_vertices(self, vertices, local_matrix, global_matrix): + return np.dot(np.dot(vertices, local_matrix.T), global_matrix.T)[:, :3] diff --git a/p5/sketch/renderer2d.py b/p5/sketch/renderer2d.py index 476bcd2c..6e5b2499 100644 --- a/p5/sketch/renderer2d.py +++ b/p5/sketch/renderer2d.py @@ -26,18 +26,14 @@ import builtins from vispy import gloo -from vispy.gloo import FrameBuffer -from vispy.gloo import IndexBuffer from vispy.gloo import Program from vispy.gloo import Texture2D from vispy.gloo import VertexBuffer from contextlib import contextmanager - -from .shaders2d import src_default -from .shaders2d import src_fbuffer from .shaders2d import src_texture from .shaders2d import src_line +from .openglrenderer import OpenGLRenderer from OpenGL.GLU import gluTessBeginPolygon, gluTessBeginContour, gluTessEndPolygon, gluTessEndContour, gluTessVertex @@ -216,83 +212,17 @@ def get_render_primitives(shape): render_primitives.extend(_get_borders(shape)) return render_primitives -class Renderer2D: +class Renderer2D(OpenGLRenderer): def __init__(self): - self.default_prog = None - self.fbuffer_prog = None + super().__init__() self.texture_prog = None self.line_prog = None - - self.fbuffer = None - self.fbuffer_tex_front = None - self.fbuffer_tex_back = None - - self.vertex_buffer = None - self.index_buffer = None - - ## Renderer Globals: USEFUL CONSTANTS - self.COLOR_WHITE = (1, 1, 1, 1) - self.COLOR_BLACK = (0, 0, 0, 1) - self.COLOR_DEFAULT_BG = (0.8, 0.8, 0.8, 1.0) - - ## Renderer Globals: STYLE/MATERIAL PROPERTIES - ## - self.background_color = self.COLOR_DEFAULT_BG - - self.fill_color = self.COLOR_WHITE - self.fill_enabled = True - - self.stroke_color = self.COLOR_BLACK - self.stroke_enabled = True - - self.tint_color = self.COLOR_BLACK - self.tint_enabled = False - - ## Renderer Globals: Curves - self.stroke_weight = 1 - self.stroke_cap = 2 - self.stroke_join = 0 - - ## Renderer Globals - ## VIEW MATRICES, ETC - ## - self.viewport = None - self.texture_viewport = None - self.transform_matrix = np.identity(4) self.modelview_matrix = np.identity(4) - self.projection_matrix = np.identity(4) - - ## Renderer Globals: RENDERING - self.draw_queue = [] def initialize_renderer(self): - self.fbuffer = FrameBuffer() - - vertices = np.array([[-1.0, -1.0], - [+1.0, -1.0], - [-1.0, +1.0], - [+1.0, +1.0]], - np.float32) - texcoords = np.array([[0.0, 0.0], - [1.0, 0.0], - [0.0, 1.0], - [1.0, 1.0]], - dtype=np.float32) - - self.fbuf_vertices = VertexBuffer(data=vertices) - self.fbuf_texcoords = VertexBuffer(data=texcoords) - - self.fbuffer_prog = Program(src_fbuffer.vert, src_fbuffer.frag) - self.fbuffer_prog['texcoord'] = self.fbuf_texcoords - self.fbuffer_prog['position'] = self.fbuf_vertices - - self.vertex_buffer = VertexBuffer() - self.index_buffer = IndexBuffer() - - self.default_prog = Program(src_default.vert, src_default.frag) + super().initialize_renderer() self.texture_prog = Program(src_texture.vert, src_texture.frag) self.texture_prog['texcoord'] = self.fbuf_texcoords - self.reset_view() def reset_view(self): @@ -389,10 +319,6 @@ def draw_loop(self): self.fbuffer_tex_front, self.fbuffer_tex_back = self.fbuffer_tex_back, self.fbuffer_tex_front - - def _transform_vertices(self, vertices, local_matrix, global_matrix): - return np.dot(np.dot(vertices, local_matrix.T), global_matrix.T)[:, :3] - def _add_to_draw_queue_simple(self, stype, vertices, idx, fill, stroke, stroke_weight, stroke_cap, stroke_join): """Adds shape of stype to draw queue """ @@ -436,52 +362,6 @@ def flush_geometry(self): self.draw_queue = [] - def render_default(self, draw_type, draw_queue): - # 1. Get the maximum number of vertices persent in the shapes - # in the draw queue. - # - if len(draw_queue) == 0: - return - - num_vertices = 0 - for vertices, _, _ in draw_queue: - num_vertices = num_vertices + len(vertices) - - # 2. Create empty buffers based on the number of vertices. - # - data = np.zeros(num_vertices, - dtype=[('position', np.float32, 3), - ('color', np.float32, 4)]) - - # 3. Loop through all the shapes in the geometry queue adding - # it's information to the buffer. - # - sidx = 0 - draw_indices = [] - for vertices, idx, color in draw_queue: - num_shape_verts = len(vertices) - - data['position'][sidx:(sidx + num_shape_verts),] = vertices - - color_array = np.array([color] * num_shape_verts) - data['color'][sidx:sidx + num_shape_verts, :] = color_array - - draw_indices.append(sidx + idx) - - sidx += num_shape_verts - - self.vertex_buffer.set_data(data) - self.index_buffer.set_data(np.hstack(draw_indices)) - - # 4. Bind the buffer to the shader. - # - self.default_prog.bind(self.vertex_buffer) - - # 5. Draw the shape using the proper shape type and get rid of - # the buffers. - # - self.default_prog.draw(draw_type, indices=self.index_buffer) - def render_line(self, queue): ''' This rendering algorithm works by tesselating the line into @@ -604,8 +484,6 @@ def cleanup(self): program is about to exit. """ - self.default_prog.delete() - self.fbuffer_prog.delete() + OpenGLRenderer.cleanup() self.line_prog.delete() - self.fbuffer.delete() diff --git a/p5/sketch/renderer3d.py b/p5/sketch/renderer3d.py index 767c6f60..13f37a69 100644 --- a/p5/sketch/renderer3d.py +++ b/p5/sketch/renderer3d.py @@ -24,96 +24,24 @@ import builtins from vispy import gloo -from vispy.gloo import FrameBuffer -from vispy.gloo import IndexBuffer -from vispy.gloo import Program from vispy.gloo import Texture2D -from vispy.gloo import VertexBuffer from contextlib import contextmanager from ..core.constants import Z_EPSILON - -from .shaders3d import src_default -from .shaders3d import src_fbuffer - from ..core.geometry import Geometry from ..core.shape import PShape from ..pmath.matrix import translation_matrix +from .openglrenderer import OpenGLRenderer -class Renderer3D: +class Renderer3D(OpenGLRenderer): def __init__(self): - self.default_prog = None - - self.fbuffer = None - self.fbuffer_tex_front = None - self.fbuffer_tex_back = None - - self.vertex_buffer = None - self.index_buffer = None - - ## Renderer Globals: USEFUL CONSTANTS - self.COLOR_WHITE = (1, 1, 1, 1) - self.COLOR_BLACK = (0, 0, 0, 1) - self.COLOR_DEFAULT_BG = (0.8, 0.8, 0.8, 1.0) - - ## Renderer Globals: STYLE/MATERIAL PROPERTIES - ## - self.background_color = self.COLOR_DEFAULT_BG - - self.fill_color = self.COLOR_WHITE - self.fill_enabled = True - - self.stroke_color = self.COLOR_BLACK - self.stroke_enabled = True - - self.tint_color = self.COLOR_BLACK - self.tint_enabled = False - - ## Renderer Globals: Curves - self.stroke_weight = 1 - self.stroke_cap = 2 - self.stroke_join = 0 - - ## Renderer Globals - ## VIEW MATRICES, ETC - ## - self.viewport = None - self.texture_viewport = None - self.transform_matrix = np.identity(4) - self.projection_matrix = np.identity(4) + super().__init__() self.lookat_matrix = np.identity(4) - ## Renderer Globals: RENDERING - self.draw_queue = [] - def initialize_renderer(self): - self.fbuffer = FrameBuffer() - - vertices = np.array([[-1.0, -1.0], - [+1.0, -1.0], - [-1.0, +1.0], - [+1.0, +1.0]], - np.float32) - texcoords = np.array([[0.0, 0.0], - [1.0, 0.0], - [0.0, 1.0], - [1.0, 1.0]], - dtype=np.float32) - - self.fbuf_vertices = VertexBuffer(data=vertices) - self.fbuf_texcoords = VertexBuffer(data=texcoords) - - self.fbuffer_prog = Program(src_fbuffer.vert, src_fbuffer.frag) - self.fbuffer_prog['texcoord'] = self.fbuf_texcoords - self.fbuffer_prog['position'] = self.fbuf_vertices - - self.vertex_buffer = VertexBuffer() - self.index_buffer = IndexBuffer() - - self.default_prog = Program(src_default.vert, src_default.frag) - + super().initialize_renderer() self.reset_view() def reset_view(self): @@ -199,9 +127,6 @@ def draw_loop(self): self.fbuffer_tex_front, self.fbuffer_tex_back = self.fbuffer_tex_back, self.fbuffer_tex_front - def _transform_vertices(self, vertices, local_matrix, global_matrix): - return np.dot(np.dot(vertices, local_matrix.T), global_matrix.T)[:, :3] - def render(self, shape): if isinstance(shape, Geometry): n = len(shape.vertices) @@ -320,61 +245,3 @@ def flush_geometry(self): current_queue = [] self.draw_queue = [] - - def render_default(self, draw_type, draw_queue): - # 1. Get the maximum number of vertices persent in the shapes - # in the draw queue. - # - if len(draw_queue) == 0: - return - - num_vertices = 0 - for vertices, _, _ in draw_queue: - num_vertices = num_vertices + len(vertices) - - # 2. Create empty buffers based on the number of vertices. - # - data = np.zeros(num_vertices, - dtype=[('position', np.float32, 3), - ('color', np.float32, 4)]) - - # 3. Loop through all the shapes in the geometry queue adding - # it's information to the buffer. - # - sidx = 0 - draw_indices = [] - for vertices, idx, color in draw_queue: - num_shape_verts = len(vertices) - - data['position'][sidx:(sidx + num_shape_verts),] = np.array(vertices) - - color_array = np.array([color] * num_shape_verts) - data['color'][sidx:sidx + num_shape_verts, :] = color_array - - draw_indices.append(sidx + idx) - - sidx += num_shape_verts - - self.vertex_buffer.set_data(data) - self.index_buffer.set_data(np.hstack(draw_indices)) - - # 4. Bind the buffer to the shader. - # - self.default_prog.bind(self.vertex_buffer) - - # 5. Draw the shape using the proper shape type and get rid of - # the buffers. - # - self.default_prog.draw(draw_type, indices=self.index_buffer) - - def cleanup(self): - """Run the clean-up routine for the renderer. - - This method is called when all drawing has been completed and the - program is about to exit. - - """ - self.default_prog.delete() - self.fbuffer_prog.delete() - self.fbuffer.delete() -