1
$\begingroup$

I started an add-on by copying operators and ended up using numpy. I have some raw 3D-vertex-coords and need there 2D-viewport-coords. I'd like to do the following, but with numpy:

for area in bpy.context.screen.areas:
    if area.type == 'VIEW_3D':
        viewport = area.regions[4]
        selvrts = [v for v in bpy.context.active_object.data.vertices if v.select]
        for v in selvrts:
            #coo in 3d space Blender 2.8
            co_3d = oa.matrix_world @ v.co
            #coo in the 3d view area (2d)
            co_2d = view3d_utils.location_3d_to_region_2d(viewport, area.spaces[0].region_3d, co_3d)

Looping over the coords is somehow an interruption, since I'm continuing with numpy calculations afterwards. I need to know, which connected vertex the user sees left or right from the selected vertex.

At the moment I went thus far but couldn't find the code to get the 2D-coords.

for area in bpy.context.screen.areas:
    if area.type == 'VIEW_3D':
        viewport = area.regions[4]
        # Add an extra 1.0s column (for matrix dot product)
        coB = np.c_[cB, np.ones(cB.shape[0])]
        # Then:
        # Dot product matrix with the coords transpose
        # Keep the first 3 rows (x,y,z)
        # Transpose result to Nx3
        # Flatten
        coB = np.dot(oa.matrix_world, coB.T)[0:3].T.reshape((-1))
        coB = coB.reshape(cB.shape)

How can I get 2D-viewport-coords with numpy?

$\endgroup$
1
  • 2
    $\begingroup$ The source is in scripts/modules/bpy_extras/view3d_utils.py $\endgroup$
    – batFINGER
    Commented Jan 31, 2020 at 15:53

1 Answer 1

2
$\begingroup$
import bpy
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader

import numpy as np


def to2d_array(area, coords, wm=None):
    space_data = area.spaces.active
    region_3d = space_data.region_3d
    region = area.regions[-1]

    rw, rh = region.width, region.height
    persp_mat = np.array(region_3d.perspective_matrix, 'f')

    count = coords.shape[0]
    coords4 = np.c_[coords, np.ones(count, 'f')]

    if wm is not None:
        wmat = np.array(wm)
        coords4 = (wmat @ coords4.T).T

    proj = (persp_mat @ coords4.T).T

    x, y, w = proj[:, 0], proj[:, 1], proj[:, 3]
    x2d = rw * (1 + x / w) / 2.0
    y2d = rh * (1 + y / w) / 2.0

    return np.stack([x2d, y2d]).T.astype(int)


def draw_callback_px():
    context = bpy.context
    mesh = context.object
    if mesh and mesh.type == 'MESH' and context.area.type == 'VIEW_3D':
        vcount = len(mesh.data.vertices)
        coords = np.zeros(vcount * 3, 'f')
        mesh.data.vertices.foreach_get("co", coords)

        coords.shape = vcount, 3

        mesh_coords2d = to2d_array(context.area, coords, mesh.matrix_world).tolist()

        # 50% alpha, 2 pixel width line
        shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glLineWidth(2)
        batch = batch_for_shader(shader, 'POINTS', {"pos": mesh_coords2d})
        shader.bind()
        shader.uniform_float("color", (1.0, 1.0, 0.0, 1.0))
        batch.draw(shader)

        # restore opengl defaults
        bgl.glLineWidth(1)
        bgl.glDisable(bgl.GL_BLEND)

        print("coords2d: ", mesh_coords2d)


bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
$\endgroup$

You must log in to answer this question.

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