Importing internal function "snapObjectsEx"

Hello,
I’m starting to understand how to use the internal C functions within the Blender API in Python.
And to begin, I decided to import a function that I find very useful. It’s the “snapObjectsex”, referenced in the header: c:\BlenderDEV\blender\source\blender\editors\include\ED_transform.h
This function is used by the operator ruler for example.

And I got, Look:
https://lh3.googleusercontent.com/-sCfe3RUKRjI/Vkd69hLFtTI/AAAAAAAABic/IAJGdOYBzfE/w635-h328-no/snap_test.gif

But as you can see, I’m having trouble with the snap mode: face.
WHYYY???

The Cython function that I used was this:


from libc.stdint cimport intptr_t


#C:\BlenderDEV\blender\source\blender\blenkernel\BKE_bvhutils.h
#cdef extern from "BKE_bvhutils.h":
#    ctypedef struct BVHTreeFromMesh:
#        pass
#    ctypedef struct BVHTreeNearest:
#        pass


#C:\BlenderDEV\blender\source\blender\makesdna\DNA_space_types.h
#cdef extern from "DNA_space_types.h":
#    pass


#C:\BlenderDEV\blender\source\blender\makesdna\DNA_scene_types.h
cdef extern from "DNA_scene_types.h":
    cdef short SCE_SNAP_MODE_VERTEX
    cdef short SCE_SNAP_MODE_EDGE
    cdef short SCE_SNAP_MODE_FACE
    ctypedef struct Base:
        pass
    ctypedef struct Scene:
        float cursor[3]


#C:\BlenderDEV\blender\source\blender\makesdna\DNA_view3d_types.h
cdef extern from "DNA_view3d_types.h":
    ctypedef struct RegionView3D:
        pass
    ctypedef struct View3D:
        float viewquat[4]


#C:\BlenderDEV\blender\source\blender\makesdna\DNA_screen_types.h
cdef extern from "DNA_screen_types.h":
    ctypedef struct ScrArea:
        pass
    ctypedef struct ARegion:
        short winx, winy


#C:\BlenderDEV\blender\source\blender\makesdna\DNA_object_types.h
cdef extern from "DNA_object_types.h":
    ctypedef struct BoundBox:
        pass
    ctypedef struct Object:
        pass


#C:\BlenderDEV\blender\source\blender\blenkernel\BKE_context.h
cdef extern from "BKE_context.h":
    ctypedef struct bContext:
        pass
    ScrArea *CTX_wm_area(const bContext *C)
    ARegion *CTX_wm_region(const bContext *C)
    View3D *CTX_wm_view3d(const bContext *C)
    Scene *CTX_data_scene(const bContext *C)
    Object *CTX_data_edit_object(const bContext *C)


#C:\BlenderDEV\blender\source\blender\editors\include\ED_transform.h
cdef extern from "ED_transform.h":
    ctypedef enum SnapMode:
        SNAP_ALL = 0,
        SNAP_NOT_SELECTED = 1,
        SNAP_NOT_OBEDIT = 2


    bint snapObjectsEx(
        Scene *scene,
        Base *base_act,
        View3D *v3d,
        ARegion *ar,
        Object *obedit,
        short snap_mode,
        const float mval[2],
        float *r_dist_px,
        float r_loc[3],
        float r_no[3],
        float *r_ray_dist,
        SnapMode mode)


#C:\BlenderDEV\blender\source\blender\python\intern\bpy_util.h
#cdef extern from "bpy_util.h":
#    bContext *BPy_GetContext()


def test_snap(PyCtx, const float mval_x, const float mval_y, bint use_vert, bint use_edge, bint use_face, bint use_depth = True):
    #cdef intptr_t pointer = PyCtx.as_pointer()
    #cdef bContext *C = (<bContext*>pointer)
    
    cdef intptr_t scenePointer = PyCtx.scene.as_pointer()
    cdef Scene *scene = (<Scene*>scenePointer)
    cdef intptr_t v3dPointer = PyCtx.space_data.as_pointer()
    cdef View3D *v3d = (<View3D*>v3dPointer)
    cdef intptr_t arPointer = PyCtx.region.as_pointer()
    cdef ARegion *ar = (<ARegion*>arPointer)
    #cdef bContext *C = <bContext *>BPy_GetContext()
    #if C == NULL:
    #    print("Context is None, cant poll any operators")
    #    return
    #cdef Scene *scene = CTX_data_scene(C)
    #cdef View3D *v3d = CTX_wm_view3d(C)
    #cdef ARegion *ar = CTX_wm_region(C)
    #cdef Object *obedit = CTX_data_edit_object(C)


    cdef float r_co[3]
    r_co = 0.0,0.0,0.0
    cdef float co_ss[2]
    co_ss = mval_x, mval_y
    cdef float dist_px = 12.0 # snap dist #
    cdef float r_no_dummy[3]
    cdef float ray_dist = 1.701411733e+38
    cdef bint ret = False
    cdef float *r_no_ptr = r_no_dummy
    cdef short elem = -1


    if use_vert:
        if snapObjectsEx(scene, NULL, v3d, ar, NULL, SCE_SNAP_MODE_VERTEX,
                        co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL):
            elem = 0
            ret = True
    if use_edge and (not ret or use_depth):
        if not use_depth: ray_dist = 1.701411733e+38
        if snapObjectsEx(scene, NULL, v3d, ar, NULL, SCE_SNAP_MODE_EDGE,
                        co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL):
            elem = 1
            ret = True
    if use_face and (not ret or use_depth):
        if not use_depth: ray_dist = 1.701411733e+38
        if snapObjectsEx(scene, NULL, v3d, ar, NULL, SCE_SNAP_MODE_FACE,
                        co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL):
            elem = 2
            ret = True
    print(scene.cursor)
    print(v3d.viewquat)
    print(ar.winx, ar.winy)
    return elem, r_co


Here is the compiled module for 64-bit Windows:

And here’s a script that uses the module:


import bpy, bgl
from get_context import test_snap


class ModalOperator(bpy.types.Operator):
    """Move an object with the mouse, example"""
    bl_idname = "object.snap_operator"
    bl_label = "SNAP"
    bl_options = {'REGISTER', 'UNDO'}


    def draw_callback_px(self, context):
        if self.elem != -1:
            bgl.glEnable(bgl.GL_BLEND)
            if self.elem == 0:
                bgl.glColor4f(1.0,0.0,0.0,0.5)
            if self.elem == 1:
                bgl.glColor4f(0.0,1.0,0.0,0.5)
            if self.elem == 2:
                bgl.glColor4f(0.0,0.0,1.0,0.5)            
            bgl.glDepthRange(0,0)
            bgl.glPointSize(12)
            bgl.glBegin(bgl.GL_POINTS)
            bgl.glVertex3f(*self.co)
            bgl.glEnd()


            # restore opengl defaults
            bgl.glDepthRange(0,1)
            bgl.glPointSize(1)
            bgl.glDisable(bgl.GL_BLEND)
            bgl.glColor4f(0.0, 0.0, 0.0, 1.0)


    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':
            context.area.tag_redraw()
            self.elem, self.co = test_snap(context, event.mouse_region_x, event.mouse_region_y,
                                 self.use_vert, self.use_edge, self.use_face, self.use_depth)
            print(self.elem, self.co)


        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'}


        return {'PASS_THROUGH'}


    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
    
            self.use_vert = True
            self.use_edge = True
            self.use_face = False
            self.use_depth = False
            self.elem = -1
            self.ret = False


            self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (context,), 'WINDOW', 'POST_VIEW')
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}




def register():
    bpy.utils.register_class(ModalOperator)




def unregister():
    bpy.utils.unregister_class(ModalOperator)




if __name__ == "__main__":
    register()



Any other information, just ask me.

Thank you

I could have run faces once. But after tinkering a bit I could no longer T.T

Ok, I figured out the problem. I was trying to create a dynamic library from functions created to serve as a static library in blender. This combination is not supported, but I could work around the problem by preventing the compiler import the “static multi-threaded C runtime library” (LBCMT.lib) and use only the “DLL-based multi-threaded C runtime library” (MSVCRT.lib).
Now it worked perfectly with no bugs :slight_smile: