4
$\begingroup$

I like to create virtual set of 3d grid points plotted/aligned on a normal vector. I'm planning to draw these points or lines using the GPU code. I've been experimenting with np.meshgrid but I can't interpret the data as proper 3d vectors. enter image description here

$\endgroup$

1 Answer 1

5
+50
$\begingroup$

before the actual code:

you can use the following code(addon) and just run the poly_grid operator by running this line:

bpy.ops.object.poly_grid()

and you finish the showing up by pressing ESC key.

you can also manipulate the grid parameters:

x_grid_size, y_grid_size, x_grid_count, y_grid_count, grid_color

and this is the result:

enter image description here

and this is the code:

from bmesh import types
import bpy, bgl, gpu
from bpy.types import Object
from gpu_extras.batch import batch_for_shader
from mathutils import Vector as vec

bl_info = {
    "name": "Poly Grid",
    "blender": (2, 80, 0),
    "category": "Object",
}

x_grid_size, y_grid_size = 1, 1
x_grid_count, y_grid_count = 10, 10
grid_color = vec((1,1,1,1))

def add_draw_handler(handler_function, argument):
    args = (argument,) # should be a tupple
    handler = bpy.types.SpaceView3D.draw_handler_add(\
                        handler_function,
                        args,
                        'WINDOW',
                        'POST_VIEW')
    return handler

def remove_draw_handler(handler):
    if handler != None:
        bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')

def draw_solid_lines(grid_data : 'GridData'):
    v_global_co, seg_indices = grid_data.get_line_data()
    if v_global_co != None or seg_indices != None:
        line_color = grid_color.to_tuple()
        shader_cut  = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
        shader_cut.bind()
        shader_cut.uniform_float("color", line_color)
        batch_cut   = batch_for_shader( shader_cut,
                                        'LINES',
                                        {"pos": v_global_co},
                                        indices= seg_indices)
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glEnable(bgl.GL_DEPTH_TEST)
        batch_cut.draw(shader_cut)
        bgl.glDisable(bgl.GL_DEPTH_TEST)
        bgl.glDisable(bgl.GL_BLEND)
        redraw_scene()

def redraw_scene():
    bpy.context.scene.update_tag()


class GridData:
    def __init__(self, obj):
        self.x_size = abs(x_grid_size)
        self.y_size = abs(y_grid_size)
        self.x_count = abs(x_grid_count)
        self.y_count = abs(y_grid_count)
        self.obj = obj

    def get_center_normal(self):
        obj = self.obj
        obj.update_from_editmode()
        center = vec()
        normal = vec()
        for poly in obj.data.polygons[:]:
            if poly.select:
                normal = poly.normal
                for v_i in poly.vertices:
                    center += obj.data.vertices[:][v_i].co
                center /= len(poly.vertices)
                center = obj.matrix_world @ center
                normal = obj.matrix_world @ normal
                return center, normal
        return None, None

    def get_line_data(self):
        center, normal = self.get_center_normal()
        if center == None or normal == None:
            return None , None
        world_up = vec((0,0,1))
        world_up = world_up if world_up.cross(normal).length > 0.01 else vec((0,-1,0))
        x_dir:vec = world_up.cross(normal).normalized()
        y_dir:vec = normal.cross(x_dir)
        zero_point = - x_dir * self.x_size * int(self.x_count / 2) - y_dir * self.y_size * int(self.y_count / 2)
        zero_point += center
        v_global_co = list()
        # horizontal lines
        length = self.x_count * self.x_size * x_dir
        for j in range(self.y_count + 1):
            v1 = zero_point + j * self.y_size * y_dir
            v2 = length + v1
            v_global_co.append(v1)
            v_global_co.append(v2)
        # vertical lines
        height = self.y_count * self.y_size * y_dir
        for i in range(self.x_count + 1):
            v1 = zero_point + i * self.x_size * x_dir
            v2 = height + v1
            v_global_co.append(v1)
            v_global_co.append(v2)
        # segment indices
        indices = list(range(len(v_global_co)))
        seg_indices = list(zip(indices[::2], indices[1::2]))
        return v_global_co, seg_indices

class GRID_OT_PolyGrid(bpy.types.Operator):
    """Places a grid on selected polygon"""
    bl_idname = "object.poly_grid"
    bl_label = "PolyGrid"
    bl_options = {'REGISTER'}

    @classmethod
    def poll(cls, context):
        return context.object is not None and context.object.type == 'MESH'
    
    def execute(self, context):
        self.obj = bpy.context.object
        self.grid_data = GridData(self.obj)
        self.handler = add_draw_handler(draw_solid_lines, self.grid_data)
        #if context.space_data.type == 'VIEW_3D':
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        return self.execute(context)

    def modal(self,context,event:bpy.types.Event):
        if event.type == 'ESC':
            return self.finalize(context)
        return {'PASS_THROUGH'}

    def finalize(self, context):
        remove_draw_handler(self.handler)
        del(self.grid_data)
        self.report({'INFO'}, message = "Poly grid removed")
        return {'CANCELLED'}
    
    def cancel(self, context):
        return {'CANCELLED'}

if __name__ == "__main__":
    bpy.utils.register_class(GRID_OT_PolyGrid)
$\endgroup$
0

You must log in to answer this question.

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