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$
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.