5
$\begingroup$

I know how to draw lines, points, 2d shape like rectangle, but how to draw shape by vertices from selected face? So basically, I need to redraw selected faces with 'gpu.state.depth_test_set("NONE")' to see selection through any objects in view.

$\endgroup$

3 Answers 3

5
$\begingroup$

Draw a alway on top n-gon in draw_handler 'POST_VIEW'

enter image description here

Please note that if the object position is not (0, 0, 0), you need to multiply the objects matrix_world: obj.matrix_world @ vert.co
And the selection must be linked

import bpy, gpu, bmesh
from gpu_extras.batch import batch_for_shader

# function from https://blender.stackexchange.com/questions/289911/how-to-copy-a-few-faces-from-one-bmesh-to-another
def from_faces_linked(faces_linked):
    
    # set gets unique elements, list gets ordered elements    
    vertices = list(set(v for f in faces_linked for v in f.verts))

    # establishes a map between the new mesh vertex indices and 
    # actual mesh vertex indices
    vmap = dict()
    for i, vert in enumerate(vertices):
        vmap[vert.index] = i
    
    coordinates = [v.co for v in vertices]
    
    # build faces with new indices
    faces = []
    for face in faces_linked:
        faces.append([vmap[v.index] for v in face.verts])

    # create a mesh from that
    mesh = bpy.data.meshes.new(name="copy")
    mesh.from_pydata(coordinates, [], faces)

    # and a bmesh from this mesh
    bm = bmesh.new()
    bm.from_mesh(mesh)
    
    bpy.data.meshes.remove(mesh)    

    return bm

def get_indices(loop_triangles):
    return [(e[0].vert.index, e[1].vert.index, e[2].vert.index) for e in loop_triangles]

ob = bpy.context.object
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(ob.data)

bm_select = from_faces_linked([f for f in bm.faces if f.select])
bmesh.ops.triangulate(bm_select, faces=bm_select.faces)

indices = get_indices(bm_select.calc_loop_triangles())
vertices = [v.co.copy() for v in bm_select.verts]

bm.free()
bm_select.free()

shader = gpu.shader.from_builtin('UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)


def draw():
    shader.uniform_float("color", (0, 0.5, 0.5, 1.0))
    batch.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
$\endgroup$
2
$\begingroup$

Thank you X Y

You gave me an idea on how to simplify the code and apply it to multiple selected objects.

So what I do here: get faces from selected objects and retrieve triangles from them, and then get vertex coords from this triangles. With this coords (by three from the list) I use draw function without indices (it no need in my case).

import bpy, gpu, bmesh
from gpu_extras.batch import batch_for_shader


vert_coords = []
for obj in bpy.context.selected_objects:
    if obj and obj.type == 'MESH':
        me = obj.data
        bpy.ops.object.mode_set(mode='EDIT')
        bm = bmesh.from_edit_mesh(me)

        # Get selected faces
        selected_faces = [f for f in bm.faces if f.select]    
        
        # Retrieve triangles from a selected faces
        for f in selected_faces:
            for i in range(len(f.verts) - 2):
                vert_coords.append(obj.matrix_world @ f.verts[0].co)
                vert_coords.append(obj.matrix_world @ f.verts[i + 1].co)
                vert_coords.append(obj.matrix_world @ f.verts[i + 2].co)                
        bmesh.update_edit_mesh(me)


shader = gpu.shader.from_builtin('UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vert_coords})


def draw():
    shader.uniform_float("color", (0, 0.5, 0.5, 1.0))
    batch.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
$\endgroup$
0
$\begingroup$

https://blender.stackexchange.com/a/305523/89484

Excellent answer! I also made a class version for remove for easy testing.

Reference

https://blender.stackexchange.com/a/76020/89484

import bpy, gpu, bmesh
from gpu_extras.batch import batch_for_shader


vert_coords = []
for obj in bpy.context.selected_objects:
    if obj and obj.type == 'MESH':
        me = obj.data
        bpy.ops.object.mode_set(mode='EDIT')
        bm = bmesh.from_edit_mesh(me)

        # Get selected faces
        selected_faces = [f for f in bm.faces if f.select]    
        
        # Retrieve triangles from a selected faces
        for f in selected_faces:
            for i in range(len(f.verts) - 2):
                vert_coords.append(obj.matrix_world @ f.verts[0].co)
                vert_coords.append(obj.matrix_world @ f.verts[i + 1].co)
                vert_coords.append(obj.matrix_world @ f.verts[i + 2].co)                
        bmesh.update_edit_mesh(me)


shader = gpu.shader.from_builtin('UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vert_coords})


def draw():
    shader.uniform_float("color", (0, 0.5, 0.5, 1.0))
    batch.draw(shader)



class DrawingClass:
    def __init__(self, context):
        self.handle =bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')

    def remove_handle(self):
         bpy.types.SpaceView3D.draw_handler_remove(self.handle, 'WINDOW')

context = bpy.context             
dns = bpy.app.driver_namespace
dns["dc"] = DrawingClass(context)

How to delete In Console

>>> context = bpy.context             
>>> dns = bpy.app.driver_namespace
>>> dns['dc'].remove_handle()

In theory, it takes advantage of the specification that when saved in a namespace, it is persistent.

https://blenderartists.org/t/removing-blf-draw-text-on-error/1183441/2?u=makorin

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .