How to draw some bmesh faces using gpu module

Hello everyone, I want to upgrade my addon from 2.7 to 2.8. I need to draw some faces from bmesh over mesh in viewport. Previously it was done using bgl.glBegin(bgl.GL_POLYGONS). After many tries in 2.8 I generate vertices list and indices list for batch_for_shader, but this methon doesn’t work with n-gons, just with triangles. So, how can I draw bm.faces[0:9] for example, if some of them are tris, some quads and n-gons?

or, maybe, how to convert this script from api for using with bmesh module:

import bpy
import gpu
import numpy as np
from random import random
from gpu_extras.batch import batch_for_shader

mesh = bpy.context.active_object.data
mesh.calc_loop_triangles()

vertices = np.empty((len(mesh.vertices), 3), 'f')
indices = np.empty((len(mesh.loop_triangles), 3), 'i')

mesh.vertices.foreach_get(
    "co", np.reshape(vertices, len(mesh.vertices) * 3))
mesh.loop_triangles.foreach_get(
    "vertices", np.reshape(indices, len(mesh.loop_triangles) * 3))

vertex_colors = [(random(), random(), random(), 1) for _ in range(len(mesh.vertices))]

shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
batch = batch_for_shader(
    shader, 'TRIS',
    {"pos": vertices, "color": vertex_colors},
    indices=indices,
)


def draw():
    batch.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')

For a bmesh version you can replace coords and indices with

coords = [v.co for v in bm.verts]

indices = [[loop.vert.index for loop in looptris]
            for looptris in bm.calc_loop_triangles()]

I don’t think drawing ngons reliably without triangulating is possible currently. It’s no problem generating triangle indices from convex shaped ngons, but concave ngons must first be split, either on the source mesh or on a copy.

Blender has a pretty decent internal triangulation function which is used everywhere in the program. The non-bmesh is called obj.data.calc_loop_triangles(), after which you can access these using obj.data.loop_triangles. The bmesh version is bm.calc_loop_triangles and returns the loop triangles automatically. The only issue with these functions is that they calculate triangles on the entire mesh, like the code you posted above - you can’t feed it just a couple of faces unless you separated those faces into a different mesh.

1 Like

So, if I understand right, I need to copy my faces from current bmesh to new empty bmesh and then generate coord and indices list from vertices of newly created bmesh?

Yes, save from writing your own concavity checker and index generator, it’s one quick way to deal with ngons without pre-triangulation.

How can I outline an existing circle(mesh) with 4 vertices using a modal operator?

The reason is because the circle will be moving & would like to update its location.

Hi! A simple way shown here(old file, it was not ideal, but it’s can show you how can you solve a problem)
RoughExample.blend (1.6 MB)

1 Like

I’m trying to use this method above basically, but in a modal.

I keep getting weird artifacts though for some reason… Like random verts will be at 0,0,0 or the drawing mesh will just get mangled out of no where. I am setting the faces/verts to draw when i hit a button. So it’s only drawing new face selections when I want to/setting the data. Unless that’s the issue somehow. But again, it can look “fine”, and when i’m rotating the view, suddenly will get mangled out of no where, without changing anything else…

I don’t know that it’s bad data to begin with, but something going on wrong with the shader code possibly…

Does this seem wrong anywhere?

data = self.hitObject.data
            self.hitObject.update_from_editmode()
            bm = bmesh.from_edit_mesh(data)

            faces = [f for f in bm.faces if f.select == True]

            mat = self.hitObject.matrix_world
            
            dbm = bmesh.new()
            for face in faces:
                dbm.faces.new((dbm.verts.new(mat @ v.co, v) for v in face.verts), face)
            dbm.verts.index_update()

            self.vertices = [v.co for v in dbm.verts]
            self.faceindices = [[loop.vert.index for loop in looptris] for looptris in dbm.calc_loop_triangles()]
            self.face_colors = [(0.9, 0.25, 0.25, 0.25) for _ in range(len(dbm.verts))]
            self.edgeindices = [[v.index for v in e.verts] for e in dbm.edges]
            self.edges_colors = [(0.0, 1.0, 0.25, 0.5) for _ in range(len(dbm.verts))]
batch1 = batch_for_shader(

        self.shader, 'TRIS',

        {"pos": self.vertices, "color": self.face_colors},

        indices=self.faceindices,

    )

    batch2 = batch_for_shader(

        self.shader, 'LINES',

        {"pos": self.vertices, "color": self.edges_colors},

        indices=self.edgeindices,

    )

    bgl.glEnable(bgl.GL_BLEND)

    bgl.glEnable(bgl.GL_DEPTH_TEST)  #Used to fix sorting issues/make shader not draw ontop of other objects/geo in the scene.

    bgl.glEnable(bgl.GL_CULL_FACE)

    bgl.glCullFace(bgl.GL_BACK)

    self.shader.bind()

    batch1.draw(self.shader)

    self.shader.bind()

    batch2.draw(self.shader)

    bgl.glDisable(bgl.GL_DEPTH_TEST)

    bgl.glDisable(bgl.GL_CULL_FACE)

    bgl.glLineWidth(1)

    bgl.glPointSize(1)

    bgl.glDisable(bgl.GL_BLEND)

What’s weird is, like I said, sometimes it will be working perfectly, for a while. Then suddenly it just explodes in the viewport… I noticed on 1 mesh i ran the code on, 1 vert out of about 500 was acting up. It seemed like the triangulation/order was twisted on that one possibly.

Then it was working fine again, when I ran the same code on that mesh right after…

Seems like sometimes it wants to put verts back at 0,0,0 or default matrix/original matrix. Not the new object space / offset positions…

image

Edit: I tested converting my face selection to a new mesh object and that all seems fine. So it looks like it’s something to do with how that translates into the GPU side of things / BGL, but I’m still not sure why.

@JoseConseco Any ideas? If you don’t mind :slight_smile: