You can transform the object into bmesh and use bmesh.calc_tessfaces.
Here is a code in which I tested the time of some ways to get triangles:
import ctypes
class C_BMHeader(ctypes.Structure):
_fields_ = [
("data", ctypes.c_void_p),
("index", ctypes.c_int),
("htype", ctypes.c_char),
("hflag", ctypes.c_char),
("api_flag", ctypes.c_char),
]
class C_BMLoop(ctypes.Structure):
pass
C_BMLoop._fields_ = [
("head", C_BMHeader),
("v", ctypes.POINTER(C_BMHeader)),
("e", ctypes.POINTER(C_BMHeader)),
("f", ctypes.POINTER(C_BMHeader)),
("radial_next", ctypes.POINTER(C_BMLoop)),
("radial_prev", ctypes.POINTER(C_BMLoop)),
("next", ctypes.POINTER(C_BMLoop)),
("prev", ctypes.POINTER(C_BMLoop)),
]
def get_looptris_ctypes(me):
# figure out side of _Py_ssize_t
if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
_Py_ssize_t = ctypes.c_int64
else:
_Py_ssize_t = ctypes.c_int
class _PyObject(ctypes.Structure):
pass
_PyObject._fields_ = [
('ob_refcnt', _Py_ssize_t),
('ob_type', ctypes.POINTER(_PyObject)),
]
if object.__basicsize__ != ctypes.sizeof(_PyObject):
# python with trace
class _PyObject(ctypes.Structure):
_fields_ = [
('_ob_next', ctypes.POINTER(_PyObject)),
('_ob_prev', ctypes.POINTER(_PyObject)),
('ob_refcnt', _Py_ssize_t),
('ob_type', ctypes.POINTER(_PyObject)),
]
class _PyVarObject(_PyObject):
_fields_ = [
('ob_size', _Py_ssize_t),
]
class C_BPy_BMLoop(_PyVarObject):
_fields_ = [
("bm", ctypes.c_void_p),
("l", ctypes.POINTER(C_BMLoop)),
]
#(int)(&((struct Mesh *)0)->edit_btmesh) == 272
#(int)(&((struct BMEditMesh *)0)->bm) == 0
#(int)(&((struct BMEditMesh *)0)->tottris) == 32
#(int)(&((struct BMEditMesh *)0)->looptris) == 24
edit_btmesh = ctypes.c_void_p.from_address(me.as_pointer() + 272)
if edit_btmesh.value:
ctypes.pythonapi._PyObject_New.argtypes = [ctypes.py_object]
ctypes.pythonapi._PyObject_New.restype = ctypes.py_object
import bmesh
py_loop = ctypes.pythonapi._PyObject_New(bmesh.types.BMLoop)
cpy_loop = C_BPy_BMLoop.from_address(id(py_loop))
cpy_loop.bm = ctypes.c_void_p.from_address(edit_btmesh.value + 0)
tottris = ctypes.c_int.from_address(edit_btmesh.value + 32).value
looptris = ctypes.POINTER(ctypes.POINTER(C_BMLoop) * 3 * tottris).from_address(edit_btmesh.value + 24).contents
tris = bgl.Buffer(bgl.GL_INT, (tottris, 3))
for i, ltri in enumerate(looptris):
cpy_loop.l = ltri[0]
tris[i][0] = py_loop.vert.index
cpy_loop.l = ltri[1]
tris[i][1] = py_loop.vert.index
cpy_loop.l = ltri[2]
tris[i][2] = py_loop.vert.index
return tris
def get_looptris_ctypes2(me):
#(int)(&((struct Mesh *)0)->edit_btmesh) == 272
#(int)(&((struct BMEditMesh *)0)->tottris) == 32
#(int)(&((struct BMEditMesh *)0)->looptris) == 24
edit_btmesh = ctypes.c_void_p.from_address(me.as_pointer() + 272)
if edit_btmesh.value:
tottris = ctypes.c_int.from_address(edit_btmesh.value + 32).value
looptris = ctypes.POINTER(ctypes.POINTER(C_BMLoop) * 3 * tottris).from_address(edit_btmesh.value + 24).contents
tris = bgl.Buffer(bgl.GL_INT, (tottris, 3))
for i, ltri in enumerate(looptris):
tris[i] = ltri[0].contents.v.contents.index, ltri[1].contents.v.contents.index, ltri[2].contents.v.contents.index
return tris
def get_looptris_bmesh(me):
if me.is_editmode:
bm = bmesh.from_edit_mesh(me)
looptris = bm.calc_tessface()
tris = bgl.Buffer(bgl.GL_INT, (len(looptris), 3))
for i, ltri in enumerate(looptris):
tris[i] = ltri[0].vert.index, ltri[1].vert.index, ltri[2].vert.index
return tris
def get_looptris_bmesh_numpy(me):
if me.is_editmode:
import numpy as np
bm = bmesh.from_edit_mesh(me)
looptris = bm.calc_tessface()
get_v_index = np.vectorize(lambda l: l.vert.index, otypes = ['i4'], cache = True)
tris = get_v_index(looptris)
tris = bgl.Buffer(bgl.GL_INT, tris.shape, tris)
return tris
def get_looptris_ctypes_numpy(me):
#(int)(&((struct Mesh *)0)->edit_btmesh) == 272
#(int)(&((struct BMEditMesh *)0)->tottris) == 32
#(int)(&((struct BMEditMesh *)0)->looptris) == 24
edit_btmesh_ptr = ctypes.c_void_p.from_address(me.as_pointer() + 272).value
if edit_btmesh_ptr:
bm_ltri = ctypes.POINTER(C_BMLoop) * 3
tottris = ctypes.c_int.from_address(edit_btmesh_ptr + 32).value
looptris = ctypes.POINTER(tottris * bm_ltri).from_address(edit_btmesh_ptr + 24).contents
import numpy as np
array = np.frombuffer(looptris, dtype = 'u8')
get_v_index = np.vectorize(lambda l: C_BMLoop.from_address(l).v.contents.index, otypes = ['i4'], cache = True)
tris = get_v_index(array)
tris.shape = (tottris, 3)
tris = bgl.Buffer(bgl.GL_INT, tris.shape, tris)
return tris
if __name__ == '__main__':
import bmesh, bgl, timeit
import bpy
me = bpy.context.object.data
get_looptris_ctypes_numpy(me)
print("START")
##print(timeit.timeit("get_looptris_ctypes(me)", setup="from __main__ import (get_looptris_ctypes, me)", number=5))
print(timeit.timeit("get_looptris_ctypes2(me)", setup="from __main__ import (get_looptris_ctypes2, me)", number=5))
print(timeit.timeit("get_looptris_bmesh(me)", setup="from __main__ import (get_looptris_bmesh, me)", number=5))
print(timeit.timeit("get_looptris_bmesh_numpy(me)", setup="from __main__ import (get_looptris_bmesh_numpy, me)", number=5))
print(timeit.timeit("get_looptris_ctypes_numpy(me)", setup="from __main__ import (get_looptris_ctypes_numpy, me)", number=5))
If someone has a better solution, I’d be happy to see