OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / sculpt / primitives.py

727 lines · python ref:
import bpy, mathutils, math, gpu, bmesh
from math import cos, sin
from mathutils import Vector, Matrix
from gpu_extras.batch import batch_for_shader
from ... utility import addon
from ... utility.base_modal_controls import Base_Modal_Controls
from ... ui_framework.master import Master
from ... ui_framework.utils.checks import is_mouse_in_quad
from ... utils.toggle_view3d_panels import collapse_3D_view_panels
from ... utility import method_handler
from ... utils.space_3d import get_3D_point_from_mouse, scene_ray_cast, get_3D_point_from_mouse
from ...utility.screen import dpi_factor


class Widget:
    def __init__(self):
        # Dims
        self.x_offset = 128 * dpi_factor()
        self.y_offset = 128 * dpi_factor()
        self.loc = Vector((self.x_offset, self.y_offset))
        self.radius = 18 * dpi_factor()
        # Shader
        built_in_shader = 'UNIFORM_COLOR' if bpy.app.version[0] >=4 else '2D_UNIFORM_COLOR'
        self.shader = gpu.shader.from_builtin(built_in_shader)
        # Center
        self.center_batch = None
        self.center_color = (1,1,1,.25)
        self.center_verts = []
        self.center_indices = []
        self.center_bounds = []
        self.center_loc = Vector((0,0))
        self.center_setup()
        # Line : 1
        self.l1_batch = None
        self.l1_color = (1,1,1,.25)
        self.l1_verts = []
        self.l1_indices = []
        self.l1_setup()
        # Circle
        self.circle_batch = None
        self.circle_color = (1,1,1,.25)
        self.circle_verts = []
        self.circle_indices = []
        self.circle_setup()
        # Line : 2
        self.l2_batch = None
        self.l2_color = (1,1,1,.25)
        self.l2_verts = []
        self.l2_indices = []
        self.l2_setup()
        # Square
        self.square_batch = None
        self.square_color = (1,1,1,.25)
        self.square_verts = []
        self.square_indices = []
        self.square_setup()
        # State
        self.mouse_is_over = False
        self.center_active = False
        self.circle_active = False
        self.square_active = False


    def center_setup(self):
        width = self.radius * 2
        x = self.loc[0]
        y = self.loc[1] + self.radius * 4
        self.center_verts = [
            Vector((x, y)),
            Vector((x, y + width))]
        self.center_indices = [(0,1)]
        self.center_batch = batch_for_shader(self.shader, 'LINES', {'pos': self.center_verts}, indices=self.center_indices)

        pad = 10 * dpi_factor()
        self.center_bounds = (
            (x - pad, y + width),
            (x - pad, y),
            (x + pad, y + width),
            (x + pad, y))

        self.center_loc = Vector((x, self.loc[1] + self.radius * 2))


    def l1_setup(self):
        width = self.radius * 2
        x = self.loc[0] - self.radius
        y = self.loc[1] + self.radius * 3.5
        self.l1_verts = [
            Vector((x, y)),
            Vector((x + width, y))]
        self.l1_indices = [(0,1)]
        self.l1_batch = batch_for_shader(self.shader, 'LINES', {'pos': self.l1_verts}, indices=self.l1_indices)


    def circle_setup(self):
        segments = 32
        for i in range(segments):
            index = i + 1
            angle = i * 6.28318 / segments
            x = cos(angle) * self.radius
            y = sin(angle) * self.radius
            vert = Vector((x, y + self.radius * 2))
            vert += self.loc
            self.circle_verts.append(vert)
            if(index == segments): self.circle_indices.append((i, 0))
            else: self.circle_indices.append((i, i + 1))
        summed = Vector((0,0))
        for p in self.circle_verts:
            summed += p
        self.circle_center = summed / len(self.circle_verts)
        self.circle_batch = batch_for_shader(self.shader, 'LINES', {'pos': self.circle_verts}, indices=self.circle_indices)


    def l2_setup(self):
        width = self.radius * 2
        x = self.circle_center[0] - self.radius
        y = self.circle_center[1] - self.radius * 1.5
        self.l2_verts = [
            Vector((x, y)),
            Vector((x + width, y))]
        self.l2_indices = [(0,1)]
        self.l2_batch = batch_for_shader(self.shader, 'LINES', {'pos': self.l2_verts}, indices=self.l2_indices)


    def square_setup(self):
        width = self.radius * 2
        x = self.circle_center[0] - self.radius
        y = self.circle_center[1] - self.radius * 2

        self.square_verts = [
            Vector((x, y - width)),         # 0 Bot Left
            Vector((x, y)),                 # 1 Top Left
            Vector((x + width, y)),         # 2 Top Right
            Vector((x + width, y - width))] # 3 Bot Right
        self.square_indices = [
            (0, 1),
            (1, 2),
            (2, 3),
            (3, 0)]
        self.square_batch = batch_for_shader(self.shader, 'LINES', {'pos': self.square_verts}, indices=self.square_indices)


    def update(self, context, event):

        mouse_pos = Vector((event.mouse_region_x, event.mouse_region_y))

        # Center
        if is_mouse_in_quad(self.center_bounds, mouse_pos):
            self.center_active = True
            self.center_color = (1,1,1,1)
        else:
            self.center_active = False
            self.center_color = (1,1,1,.25)

        # Circle
        if (mouse_pos - self.circle_center).magnitude <= self.radius:
            self.circle_active = True
            self.circle_color = (1,1,1,1)
        else:
            self.circle_active = False
            self.circle_color = (1,1,1,.25)

        # Square
        if is_mouse_in_quad((self.square_verts[1], self.square_verts[0], self.square_verts[2], self.square_verts[3]), mouse_pos):
            self.square_active = True
            self.square_color = (1,1,1,1)
        else:
            self.square_active = False
            self.square_color = (1,1,1,.25)

        # Mouse over
        if self.square_active or self.circle_active:
            self.mouse_is_over = True
        else: self.mouse_is_over = False


    def draw(self):
        # GL Settings
        #Enable(GL_LINE_SMOOTH)
        gpu.state.blend_set('ALPHA')
        gpu.state.line_width_set(3)
        # Center
        self.shader.bind()
        self.shader.uniform_float('color', self.center_color)
        self.center_batch.draw(self.shader)
        # Line 1
        self.shader.bind()
        self.shader.uniform_float('color', self.l1_color)
        self.l1_batch.draw(self.shader)
        # Circle
        self.shader.bind()
        self.shader.uniform_float('color', self.circle_color)
        self.circle_batch.draw(self.shader)
        # Line 2
        self.shader.bind()
        self.shader.uniform_float('color', self.l2_color)
        self.l2_batch.draw(self.shader)
        # Square
        self.shader.bind()
        self.shader.uniform_float('color', self.square_color)
        self.square_batch.draw(self.shader)


# Session Data
OBJ_SCALE = 1


class HOPS_OT_Sculpt_Primitives(bpy.types.Operator):
    bl_idname = "hops.sculpt_primitives"
    bl_label = "Sculpt Primitives"
    bl_description = """Sculpt Primitives

    Add primitives in sculpt mode

    Press H for help
    """
    bl_options = {"REGISTER", "UNDO", "BLOCKING"}

    @classmethod
    def poll(cls, context):

        return context.mode in {'OBJECT', 'SCULPT'}


    def invoke(self, context, event):
        # Props
        self.switch_sculpt = True
        self.sculpt_start = False
        self.created_target = False
        self.separate = addon.preference().property.add_primitive_newobject
        self.target_obj = context.active_object
        self.mode = context.mode
        self.shape = 'SPHERE'
        self.avg_center = Vector((0,0,0))
        self.centering = False
        self.can_center_from_widget = True
        global OBJ_SCALE
        self.scale = OBJ_SCALE
        self.location = Vector((0,0,0))
        self.graphics = True
        self.transitional_point = Vector((0,0,0))
        self.dyno_exit = bpy.context.active_object.use_dynamic_topology_sculpting #if self.sculpt_start else False
        self.notify = lambda val: bpy.ops.hops.display_notification(info=val) if addon.preference().ui.Hops_extra_info else lambda val: None

        self.use_mirror_x = False
        self.use_mirror_y = False
        self.use_mirror_z = False

        if context.mode == 'OBJECT' and not context.active_object:
            bpy.ops.object.select_all(action='DESELECT')
            self.target_obj = bpy.data.objects.new('target', None)
            context.collection.objects.link(self.target_obj)
            context.view_layer.objects.active = self.target_obj
            self.target_obj.hide_set(True)
            self.created_target = True
            self.separate = addon.preference().property.add_primitive_newobject
            self.switch_sculpt = True

        else:
            self.sculpt_start = True
            self.use_mirror_x = self.target_obj.data.use_mirror_x
            self.use_mirror_y = self.target_obj.data.use_mirror_y
            self.use_mirror_z = self.target_obj.data.use_mirror_z

        # Mode set
        if self.mode not in {'OBJECT', 'SCULPT'}:
            bpy.ops.object.mode_set(mode='SCULPT')

        # Widget
        self.shape_widget = Widget()

        # Raycast
        self.setup(context)

        # Graphics
        self.graphics_setup()

        # Base Systems
        self.master = Master(context=context)
        self.master.only_use_fast_ui = True
        self.base_controls = Base_Modal_Controls(context, event)
        self.original_tool_shelf, self.original_n_panel = collapse_3D_view_panels(force=True)
        self.draw_handle_2D = bpy.types.SpaceView3D.draw_handler_add(self.safe_draw_2D, (context,), 'WINDOW', 'POST_PIXEL')
        self.draw_handle_3D = bpy.types.SpaceView3D.draw_handler_add(self.safe_draw_3D, (context,), 'WINDOW', 'POST_VIEW')

        context.window_manager.modal_handler_add(self)
        return {"RUNNING_MODAL"}


    def modal(self, context, event):

        #--- Base Systems ---#
        self.master.receive_event(event=event)
        self.base_controls.update(context, event)

        #--- Widgets ---#
        if self.can_center_from_widget == False:
            mouse_pos = Vector((event.mouse_region_x, event.mouse_region_y))
            if (self.shape_widget.center_loc - mouse_pos).magnitude > 90 * dpi_factor():
                self.can_center_from_widget = True

        self.shape_widget.update(context, event)
        if self.shape_widget.center_active:
            if self.can_center_from_widget:
                self.can_center_from_widget = False
                self.centering = not self.centering
        elif self.shape_widget.circle_active:
            self.shape = 'SPHERE'
        elif self.shape_widget.square_active:
            self.shape = 'CUBE'

        #--- Casting ---#
        if event.shift:
            self.scale_adjust(context, event)
        else:
            self.caster(context, event)
            self.mod_controller()

        #--- Base Controls ---#
        if self.base_controls.pass_through or event.type in {"WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
            return {'PASS_THROUGH'}

        elif self.base_controls.cancel:
            self.caster_cancel()
            self.remove_shaders()
            collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel, force=True)
            self.master.run_fade()
            return {'CANCELLED'}

        elif self.base_controls.confirm:
            if not self.shape_widget.mouse_is_over:
                global OBJ_SCALE
                OBJ_SCALE = self.scale

                self.caster_confirm(context)
                self.remove_shaders()
                collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel, force=True)
                self.master.run_fade()
                return {'FINISHED'}

        #--- Modal Controls ---#
        if event.type == 'C' and event.value == 'PRESS':
            self.centering = not self.centering

        if event.type == 'S' and event.value == 'PRESS':
            if self.shape == 'SPHERE':
                self.shape = 'CUBE'
                self.sphereOBJ.hide_set(True)
                self.cubeOBJ.hide_set(False)
            elif self.shape == 'CUBE':
                self.shape = 'SPHERE'
                self.sphereOBJ.hide_set(False)
                self.cubeOBJ.hide_set(True)

        elif event.type == 'G' and event.value == 'PRESS':
            self.graphics = not self.graphics

        elif event.type == 'D' and event.value == 'PRESS':
            self.dyno_exit = not self.dyno_exit
            val = f"Dyntopo Exit: {self.dyno_exit}"
            self.notify(val)

        elif event.type == 'LEFT_BRACKET' and event.value == 'PRESS':
            self.scale -= .125
            self.set_scale()

        elif event.type == 'RIGHT_BRACKET' and event.value == 'PRESS':
            self.scale += .125
            self.set_scale()

        elif event.type == 'P' and event.value == 'PRESS' and self.sculpt_start:
            self.separate = not self.separate

            val = "Object: Separated" if self.separate else "Object: Joined"
            self.notify(val)

        elif event.type == 'K' and event.value == 'PRESS' and (self.separate or not self.sculpt_start):
            self.switch_sculpt = not self.switch_sculpt
            self.notify(f"Sculpt primitive: {self.switch_sculpt}")

        self.interface(context=context)
        context.area.tag_redraw()
        return {"RUNNING_MODAL"}


    def interface(self, context):
        self.master.setup()

        #---  Fast UI ---#
        if self.master.should_build_fast_ui():

            win_list = [
                f"Shape: {self.shape}",
                f"Center: {self.centering}",
                "Scale: {:.3f}".format(self.scale)]
            if self.sculpt_start:
                win_list.append("[P] NEW" if self.separate else "[P] ADD")
            # if self.separate or not self.sculpt_start:
            #     win_list.append(  f"Sculpt primitive: {self.switch_sculpt}")

            # Help
            help_items = {"GLOBAL" : [], "STANDARD" : []}
            help_items["GLOBAL"] = [
                ("M", "Toggle mods list"),
                ("H", "Toggle help"),
                ("~", "Toggle UI Display Type"),
                ("O", "Toggle viewport rendering")]

            help_items["STANDARD"] = [
                ("G"    , "Toggle Extra Graphics"),
                ("D"    , f"Dyno Exit: {self.dyno_exit}"),
                ("S"    , "Toggle Shapes"),
                ("C"    , "Toggle Centering"),
                ("Shift", "Adjust Scale")]

            if self.sculpt_start:
                help_items["STANDARD"].insert(0, ('P', f"New Object: {self.separate}"))
            if self.separate or not self.sculpt_start:
                help_items["STANDARD"].insert(0,('K', f"Switch Sculpt: {self.switch_sculpt}"))

            self.master.receive_fast_ui(win_list=win_list, help_list=help_items, image="Array")

        self.master.finished()

    #--- UTILS ---#

    def setup(self, context):

        if self.sculpt_start:
            depsgraph = context.evaluated_depsgraph_get()
            object_eval = self.target_obj.evaluated_get(depsgraph)
            mesh_eval = object_eval.data
            points = [self.target_obj.matrix_world @ v.co for v in mesh_eval.vertices]
            summed = Vector((0,0,0))
            for p in points:
                summed += p
            self.avg_center = summed / len(points) if len(points) > 1 else 1

            if type(self.avg_center) != Vector:
                self.avg_center = self.target_obj.matrix_world.translation

        # Create : CUBE
        mesh = bpy.data.meshes.new("HopsMesh")
        self.cubeOBJ = bpy.data.objects.new("HopsObj", mesh)
        context.collection.objects.link(self.cubeOBJ)
        bm = bmesh.new()
        bm.from_mesh(mesh)
        bmesh.ops.create_cube(bm, size=2, calc_uvs=False)
        bm.to_mesh(mesh)
        bm.free()

        # Create : SPHERE
        mesh = bpy.data.meshes.new("HopsMesh")
        self.sphereOBJ = bpy.data.objects.new("HopsObj", mesh)
        context.collection.objects.link(self.sphereOBJ)
        bm = bmesh.new()
        bm.from_mesh(mesh)
        bmesh.ops.create_cube(bm, size=1, calc_uvs=False)
        bmesh.ops.subdivide_edges(bm, edges=bm.edges, smooth=1, cuts=4, use_grid_fill=True, use_sphere=True)
        bm.to_mesh(mesh)
        bm.free()

        # Hide
        self.cubeOBJ.hide_set(True)

        # Mod : CUBE
        self.cube_mod = self.cubeOBJ.modifiers.new('Mirror', 'MIRROR')
        self.cube_mod.mirror_object = self.target_obj

        # Mod : SPHERE
        self.sphere_mod = self.sphereOBJ.modifiers.new('Mirror', 'MIRROR')
        self.sphere_mod.mirror_object = self.target_obj

        # symmetry
        self.cube_mod.use_axis[0] = self.sphere_mod.use_axis[0] = self.use_mirror_x
        self.cube_mod.use_axis[1] = self.sphere_mod.use_axis[1] = self.use_mirror_y
        self.cube_mod.use_axis[2] = self.sphere_mod.use_axis[2] = self.use_mirror_z
        self.cube_mod.use_bisect_axis = self.sphere_mod.use_bisect_axis = True, True, True

        self.cubeOBJ.data.use_mirror_x = self.sphereOBJ.data.use_mirror_x = self.use_mirror_x
        self.cubeOBJ.data.use_mirror_y = self.sphereOBJ.data.use_mirror_y = self.use_mirror_y
        self.cubeOBJ.data.use_mirror_z = self.sphereOBJ.data.use_mirror_z = self.use_mirror_z

        self.set_scale()


    def graphics_setup(self):
        self.center_verts = []

        self.center_indices_lines = []
        built_in_shader = 'UNIFORM_COLOR' if bpy.app.version[0] >=4 else '3D_UNIFORM_COLOR'
        self.center_shader_lines = gpu.shader.from_builtin(built_in_shader)
        self.center_batch_lines = None

        self.center_indices_tris = []
        self.center_shader_tris = gpu.shader.from_builtin(built_in_shader)
        self.center_batch_tris = None

        # Verts
        segments = 64
        radius = 8
        for i in range(segments):
            index = i + 1
            angle = i * 6.28318 / segments
            y = cos(angle) * radius
            z = sin(angle) * radius
            vert = Vector((0, y, z))
            vert += self.avg_center
            self.center_verts.append(vert)
            # Line indices
            if(index == segments): self.center_indices_lines.append((i, 0))
            else: self.center_indices_lines.append((i, i + 1))

        # Line batch
        self.center_batch_lines = batch_for_shader(self.center_shader_lines, 'LINES', {'pos': self.center_verts}, indices=self.center_indices_lines)

        # Tri indices
        for i in range(len(self.center_verts) - 1):
            self.center_indices_tris.append((0, i, i + 1))

        # Tri batch
        self.center_batch_tris = batch_for_shader(self.center_shader_tris, 'TRIS', {'pos': self.center_verts}, indices=self.center_indices_tris)


    def caster(self, context, event):

        self.sphereOBJ.hide_set(True)
        self.cubeOBJ.hide_set(True)

        if self.centering:
            self.center_casting(context, event)
            return

        point_location = None

        # Scene cast
        hit, location, normal, index, object, matrix = scene_ray_cast(context, event)
        if hit:
            point_location = location
            self.transitional_point = location

        # Plane cast
        if point_location == None:

            view_quat = context.region_data.view_rotation
            up = Vector((0,0,1))
            view_normal = view_quat @ up

            point = self.avg_center
            if self.transitional_point.length > 0:
                point = self.transitional_point

            mouse_pos = (event.mouse_region_x, event.mouse_region_y)
            location = get_3D_point_from_mouse(mouse_pos, context, point, view_normal)
            if not location: return
            point_location = location

        self.location = location
        self.set_locations()
        self.set_hide_states()


    def center_casting(self, context, event):
        mouse_pos = (event.mouse_region_x, event.mouse_region_y)
        location = get_3D_point_from_mouse(mouse_pos, context, self.avg_center, Vector((1,0,0)))
        if not location: return
        self.location = location

        self.set_locations()
        self.set_hide_states()


    def set_locations(self):
        self.sphereOBJ.location = self.location
        self.cubeOBJ.location = self.location


    def set_hide_states(self):
        if self.shape == 'SPHERE':
            self.sphereOBJ.hide_set(False)
        elif self.shape == 'CUBE':
            self.cubeOBJ.hide_set(False)


    def mod_controller(self):

        obj = self.sphereOBJ if self.shape == 'SPHERE' else self.cubeOBJ
        mod = self.sphere_mod if self.shape == 'SPHERE' else self.cube_mod

        mod.show_viewport = not self.centering

        mod.use_bisect_flip_axis[0] = obj.location[0] < self.avg_center[0]
        mod.use_bisect_flip_axis[1] = obj.location[1] < self.avg_center[1]
        mod.use_bisect_flip_axis[2] = obj.location[2] < self.avg_center[2]


    def scale_adjust(self, context, event):
        view_quat = context.region_data.view_rotation
        up = Vector((0,0,1))
        view_normal = view_quat @ up

        mouse_pos = (event.mouse_region_x, event.mouse_region_y)

        location = get_3D_point_from_mouse(mouse_pos, context, self.sphereOBJ.location, view_normal)
        if not location: return
        self.scale = (location - self.location).magnitude
        self.set_scale()


    def set_scale(self):
        self.cubeOBJ.scale = Vector((self.scale, self.scale, self.scale))
        self.sphereOBJ.scale = Vector((self.scale, self.scale, self.scale))


    def caster_confirm(self, context):

        if self.mode != 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.select_all(action='DESELECT')

        new_object = None

        if self.shape == 'SPHERE':
            bpy.data.objects.remove(self.cubeOBJ)
            self.sphereOBJ.select_set(True)
            context.view_layer.objects.active = self.sphereOBJ
            new_object = self.sphereOBJ
            if self.sphere_mod.show_viewport: bpy.ops.object.modifier_apply(modifier=self.sphere_mod.name)

        elif self.shape == 'CUBE':
            bpy.data.objects.remove(self.sphereOBJ)
            self.cubeOBJ.select_set(True)
            context.view_layer.objects.active = self.cubeOBJ
            new_object = self.cubeOBJ
            if self.cube_mod.show_viewport: bpy.ops.object.modifier_apply(modifier=self.cube_mod.name)

        if self.sculpt_start:
            if self.separate:
                if self.switch_sculpt:
                    new_object.select_set(True)
                    context.view_layer.objects.active = new_object
                    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
                    bpy.ops.object.mode_set(mode='SCULPT')
                    if self.dyno_exit:
                        bpy.ops.sculpt.dynamic_topology_toggle()

                else:
                    new_object.select_set(False)
                    context.view_layer.objects.active = self.target_obj
                    self.target_obj.select_set(True)
                    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
                    bpy.ops.object.mode_set(mode='SCULPT')

            else:
                self.target_obj.select_set(True)
                context.view_layer.objects.active = self.target_obj
                bpy.ops.object.join()
                bpy.ops.object.mode_set(mode='SCULPT')

                if self.dyno_exit:
                    bpy.ops.sculpt.dynamic_topology_toggle()
        else:
            bpy.ops.object.select_all(action='DESELECT')
            new_object.select_set(True)
            context.view_layer.objects.active = new_object
            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
            if self.switch_sculpt:
                bpy.ops.object.mode_set(mode='SCULPT')


        if self.created_target:
            bpy.data.objects.remove(self.target_obj)

    def caster_cancel(self):
        bpy.data.objects.remove(self.cubeOBJ)
        bpy.data.objects.remove(self.sphereOBJ)

        if self.created_target:
            bpy.data.objects.remove(self.target_obj)

    #--- SHADERS ---#

    def remove_shaders(self):
        if self.draw_handle_2D:
            self.draw_handle_2D = bpy.types.SpaceView3D.draw_handler_remove(self.draw_handle_2D, "WINDOW")
        if self.draw_handle_3D:
            self.draw_handle_3D = bpy.types.SpaceView3D.draw_handler_remove(self.draw_handle_3D, "WINDOW")

    # 2D SHADER
    def safe_draw_2D(self, context):
        method_handler(self.draw_shader_2D,
            arguments = (context,),
            identifier = 'Modal Shader 2D',
            exit_method = self.remove_shaders)


    def draw_shader_2D(self, context):
        self.shape_widget.draw()

    # 3D SHADER
    def safe_draw_3D(self, context):
        method_handler(self.draw_shader_3D,
            arguments = (context,),
            identifier = 'Modal Shader 3D',
            exit_method = self.remove_shaders)


    def draw_shader_3D(self, context):
        if not self.graphics: return
        if self.centering:
            #Enable(GL_LINE_SMOOTH)
            gpu.state.blend_set('ALPHA')
            gpu.state.depth_test_set('LESS')
            #glDepthFunc(GL_LESS)
            gpu.state.line_width_set(3)

            self.center_shader_lines.bind()
            self.center_shader_lines.uniform_float('color', (1,1,1,.125))
            self.center_batch_lines.draw(self.center_shader_lines)

            self.center_shader_tris.bind()
            self.center_shader_tris.uniform_float('color', (1,1,1,.03125))
            self.center_batch_tris.draw(self.center_shader_tris)

            gpu.state.depth_test_set('NONE')
            gpu.state.face_culling_set('NONE')
            gpu.state.depth_test_set('NONE')