From 62c1c966f608dc90f1403beff422fe9f30ce156d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 30 Oct 2018 14:14:22 +0100 Subject: [PATCH] Attract: draw using the GPU module The drawing is rather primitive, but it works. --- blender_cloud/attract/draw.py | 157 ++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 43 deletions(-) diff --git a/blender_cloud/attract/draw.py b/blender_cloud/attract/draw.py index 1150e2d..733dd4f 100644 --- a/blender_cloud/attract/draw.py +++ b/blender_cloud/attract/draw.py @@ -18,9 +18,12 @@ # -import bpy import logging -import collections +import typing + +import bpy +import bgl +import gpu log = logging.getLogger(__name__) @@ -34,10 +37,74 @@ strip_status_colour = { 'todo': (1.0, 0.5019607843137255, 0.5019607843137255) } -CONFLICT_COLOUR = (0.576, 0.118, 0.035) # RGB tuple +CONFLICT_COLOUR = (0.576, 0.118, 0.035, 1.0) # RGBA tuple + +gpu_vertex_shader = ''' +uniform mat4 ModelViewProjectionMatrix; + +layout (location = 0) in vec2 pos; +layout (location = 1) in vec4 color; + +out vec4 lineColor; // output to the fragment shader + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0.0, 1.0); + lineColor = color; +} +''' + +gpu_fragment_shader = ''' +out vec4 fragColor; +in vec4 lineColor; + +void main() +{ + fragColor = lineColor; +} +''' + +Float2 = typing.Tuple[float, float] +Float3 = typing.Tuple[float, float, float] +Float4 = typing.Tuple[float, float, float, float] -def get_strip_rectf(strip): +class AttractLineDrawer: + + def __init__(self): + self._format = gpu.types.GPUVertFormat() + self._pos_id = self._format.attr_add( + id="pos", + comp_type="F32", + len=2, + fetch_mode="FLOAT") + self._color_id = self._format.attr_add( + id="color", + comp_type="F32", + len=4, + fetch_mode="FLOAT") + + self.shader = gpu.types.GPUShader(gpu_vertex_shader, gpu_fragment_shader) + + def draw(self, + coords: typing.List[Float2], + colors: typing.List[Float4]): + if not coords: + return + + bgl.glEnable(bgl.GL_BLEND) + bgl.glLineWidth(2.0) + + vbo = gpu.types.GPUVertBuf(len=len(coords), format=self._format) + vbo.attr_fill(id=self._pos_id, data=coords) + vbo.attr_fill(id=self._color_id, data=colors) + + batch = gpu.types.GPUBatch(type="LINES", buf=vbo) + batch.program_set(self.shader) + batch.draw() + + +def get_strip_rectf(strip) -> Float4: # Get x and y in terms of the grid's frames and channels x1 = strip.frame_final_start x2 = strip.frame_final_end @@ -47,59 +114,56 @@ def get_strip_rectf(strip): return x1, y1, x2, y2 -def draw_underline_in_strip(strip_coords, pixel_size_x, color): - from bgl import glColor4f, glRectf, glEnable, glDisable, GL_BLEND - import bgl - - context = bpy.context - +def underline_in_strip(strip_coords: Float4, + pixel_size_x: float, + color: Float4, + out_coords: typing.List[Float2], + out_colors: typing.List[Float4]): # Strip coords s_x1, s_y1, s_x2, s_y2 = strip_coords # be careful not to draw over the current frame line - cf_x = context.scene.frame_current_final + cf_x = bpy.context.scene.frame_current_final - bgl.glPushAttrib(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_LINE_BIT) + # TODO(Sybren): figure out how to pass one colour per line, + # instead of one colour per vertex. + out_coords.append((s_x1, s_y1)) + out_colors.append(color) - glColor4f(*color) - glEnable(GL_BLEND) - bgl.glLineWidth(2) - bgl.glBegin(bgl.GL_LINES) - - bgl.glVertex2f(s_x1, s_y1) if s_x1 < cf_x < s_x2: - # Bad luck, the line passes our strip - bgl.glVertex2f(cf_x - pixel_size_x, s_y1) - bgl.glVertex2f(cf_x + pixel_size_x, s_y1) - bgl.glVertex2f(s_x2, s_y1) + # Bad luck, the line passes our strip, so draw two lines. + out_coords.append((cf_x - pixel_size_x, s_y1)) + out_colors.append(color) - bgl.glEnd() - bgl.glPopAttrib() + out_coords.append((cf_x + pixel_size_x, s_y1)) + out_colors.append(color) + + out_coords.append((s_x2, s_y1)) + out_colors.append(color) -def draw_strip_conflict(strip_coords, pixel_size_x): +def strip_conflict(strip_coords: Float4, + out_coords: typing.List[Float2], + out_colors: typing.List[Float4]): """Draws conflicting states between strips.""" - import bgl - s_x1, s_y1, s_x2, s_y2 = strip_coords - bgl.glPushAttrib(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_LINE_BIT) - # Always draw the full rectangle, the conflict should be resolved and thus stand out. - bgl.glColor3f(*CONFLICT_COLOUR) - bgl.glLineWidth(2) + # TODO(Sybren): draw a rectangle instead of a line. + out_coords.append((s_x1, s_y2)) + out_colors.append(CONFLICT_COLOUR) - bgl.glBegin(bgl.GL_LINE_LOOP) - bgl.glVertex2f(s_x1, s_y1) - bgl.glVertex2f(s_x2, s_y1) - bgl.glVertex2f(s_x2, s_y2) - bgl.glVertex2f(s_x1, s_y2) - bgl.glEnd() + out_coords.append((s_x2, s_y1)) + out_colors.append(CONFLICT_COLOUR) - bgl.glPopAttrib() + out_coords.append((s_x2, s_y2)) + out_colors.append(CONFLICT_COLOUR) + + out_coords.append((s_x1, s_y1)) + out_colors.append(CONFLICT_COLOUR) -def draw_callback_px(): +def draw_callback_px(line_drawer: AttractLineDrawer): context = bpy.context if not context.scene.sequence_editor: @@ -115,6 +179,10 @@ def draw_callback_px(): strips = shown_strips(context) + coords: typing.List[Float2] = [] + colors: typing.List[Float4] = [] + + # Collect all the lines (vertex coords + vertex colours) to draw. for strip in strips: if not strip.atc_object_id: continue @@ -124,7 +192,7 @@ def draw_callback_px(): # check if any of the coordinates are out of bounds if strip_coords[0] > xwin2 or strip_coords[2] < xwin1 or strip_coords[1] > ywin2 or \ - strip_coords[3] < ywin1: + strip_coords[3] < ywin1: continue # Draw @@ -136,9 +204,11 @@ def draw_callback_px(): alpha = 1.0 if strip.atc_is_synced else 0.5 - draw_underline_in_strip(strip_coords, pixel_size_x, color + (alpha,)) + underline_in_strip(strip_coords, pixel_size_x, color + (alpha,), coords, colors) if strip.atc_is_synced and strip.atc_object_id_conflict: - draw_strip_conflict(strip_coords, pixel_size_x) + strip_conflict(strip_coords, coords, colors) + + line_drawer.draw(coords, colors) def tag_redraw_all_sequencer_editors(): @@ -162,8 +232,9 @@ def callback_enable(): if cb_handle: return + line_drawer = AttractLineDrawer() cb_handle[:] = bpy.types.SpaceSequenceEditor.draw_handler_add( - draw_callback_px, (), 'WINDOW', 'POST_VIEW'), + draw_callback_px, (line_drawer,), 'WINDOW', 'POST_VIEW'), tag_redraw_all_sequencer_editors()