Exporting Triangles API Challenges

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 :wink:

1 Like