Last active
November 9, 2021 23:08
-
-
Save jeacom25b/a48f00b41eca7c7a190d3b7cbf934ac2 to your computer and use it in GitHub Desktop.
Utility module for blender 2.8, a line renderer for visual debugging and line drawing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Created by Jeacom | |
This is a utility module for drawing lines in the 3D viewport on Blender 2.8 | |
using the GPU Api | |
The idea is to get rid of messy draw functions and data that is hard to keep track. | |
This class works directly like a callable draw handler and keeps track of all the geometry data. | |
''' | |
__all__ = ["LineRenderer", | |
"BLEND", | |
"MULTIPLY_BLEND", | |
"ADDITIVE_BLEND"] | |
import bpy | |
import bgl | |
import gpu | |
from gpu_extras.batch import batch_for_shader | |
from mathutils import Matrix, Vector | |
# Blend Modes | |
BLEND = 0 | |
MULTIPLY_BLEND = 1 | |
ADDITIVE_BLEND = 2 | |
class LineRenderer: | |
def __init__(self): | |
# Useful for rendering in the same space of an object | |
self.matrix = Matrix().Identity(4) | |
# X-ray mode, draw through solid objects | |
self.draw_on_top = False | |
# Blend mode to choose, set it to one of the blend constants. | |
self.blend_mode = BLEND | |
# Width of the lines | |
self.line_width = 2.5 | |
# Handler Placeholder | |
self.draw_handler = None | |
self._coords = [] | |
self._colors = [] | |
self._line_shader = gpu.shader.from_builtin("3D_SMOOTH_COLOR") | |
self._line_batch = batch_for_shader(self._line_shader, 'LINES', {"pos": self._coords, "color": self._colors}) | |
def __call__(self, *args, **kwargs): | |
# __call__ Makes this object behave like a function. | |
# So you can add it like a draw handler. | |
self._draw() | |
def setup_handler(self): | |
# Utility function to easily add it as a draw handler | |
self.draw_handler = bpy.types.SpaceView3D.draw_handler_add(self, (), "WINDOW", "POST_VIEW") | |
def remove_handler(self): | |
# Utility function to remove the handler | |
self.draw_handler = bpy.types.SpaceView3D.draw_handler_remove(self.draw_handler, "WINDOW") | |
def update_batch(self): | |
# This takes the data rebuilds the shader batch. | |
# Call it every time you clear the data or add new lines, otherwize, | |
# You wont see changes in the viewport | |
coords = [self.matrix @ coord for coord in self._coords] | |
self._line_batch = batch_for_shader(self._line_shader, 'LINES', {"pos": coords, "color": self._colors}) | |
def add_line(self, start, end, color1=(1, 0, 0, 1), color2=None): | |
# Simple add_line function, support color gradients, | |
# if only color1 is specified, it will be solid color (color1 on both ends) | |
# This doesnt render a line, it just adds the vectors and colors to the data | |
# so after calling update_batch(), it will be converted in a buffer Object | |
self._coords.append(Vector(start)) | |
self._coords.append(Vector(end)) | |
self._colors.append(color1) | |
if color2 is None: | |
self._colors.append(color1) | |
else: | |
self._colors.append(color2) | |
def clear_data(self): | |
# just clear all the data | |
self._coords.clear() | |
self._colors.clear() | |
def _start_drawing(self): | |
# This handles all the settings of the renderer before starting the draw stuff | |
if self.blend_mode == BLEND: | |
bgl.glEnable(bgl.GL_BLEND) | |
elif self.blend_mode == MULTIPLY_BLEND: | |
bgl.glEnable(bgl.GL_BLEND) | |
bgl.glBlendFunc(bgl.GL_DST_COLOR, bgl.GL_ZERO) | |
elif self.blend_mode == ADDITIVE_BLEND: | |
bgl.glEnable(bgl.GL_BLEND) | |
bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE) | |
if self.draw_on_top: | |
bgl.glDisable(bgl.GL_DEPTH_TEST) | |
bgl.glLineWidth(self.line_width) | |
def _stop_drawing(self): | |
# just reset some OpenGL stuff to not interfere with other drawings in the viewport | |
# its not absolutely necessary but makes it safer. | |
bgl.glDisable(bgl.GL_BLEND) | |
bgl.glLineWidth(1) | |
if self.draw_on_top: | |
bgl.glEnable(bgl.GL_DEPTH_TEST) | |
def _draw(self): | |
# This should be called by __call__, | |
# just regular routines for rendering in the viewport as a draw_handler | |
self._start_drawing() | |
batch = self._line_batch | |
self._line_shader.bind() | |
batch.draw(self._line_shader) | |
self._stop_drawing() | |
if __name__ == "__main__": | |
# Simple example, run it on blender's text editor. | |
# create a new instance of the class | |
renderer = LineRenderer() | |
# add lines to ir | |
renderer.add_line((10,0,0), (-10,0,0), color1=(1,0,0,1), color2=(0,0,1,1)) | |
renderer.add_line((0,0,0), (0,0,5), color1=(0,1,0,1), color2=(0,1,1,1)) | |
# enable X ray mode/see through objects and set Blend mode to Additive | |
renderer.draw_on_top = True | |
renderer.blend_mode = ADDITIVE_BLEND | |
# set line width to 5 | |
renderer.line_width = 5 | |
# Important, update batch always when adding | |
# new lines, otherwize they wont render. | |
renderer.update_batch() | |
# setup draw handler, optionally, you can call bpy.SpaceView3D.draw_handler_add() | |
renderer.setup_handler() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is really awesome, thanks!