diff options
author | mano-wii <germano.costa@ig.com.br> | 2019-02-06 08:13:10 +0300 |
---|---|---|
committer | mano-wii <germano.costa@ig.com.br> | 2019-02-06 08:13:10 +0300 |
commit | 1142e215758f00714a139769d9ff6af35da0fa6c (patch) | |
tree | a8c913474eb011b9be3619a4a1e672a183e276a1 /mesh_snap_utilities_line | |
parent | afd211e1089fecb4ce6ff01c3967673e999dc1d7 (diff) |
mesh_snap_utilties_line: Add Gizmos support
Diffstat (limited to 'mesh_snap_utilities_line')
-rw-r--r-- | mesh_snap_utilities_line/__init__.py | 92 | ||||
-rw-r--r-- | mesh_snap_utilities_line/common_classes.py | 465 | ||||
-rw-r--r-- | mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat (renamed from mesh_snap_utilities_line/icons/ops.mesh.make_line.dat) | bin | 1340 -> 1340 bytes | |||
-rw-r--r-- | mesh_snap_utilities_line/ops_line.py | 210 | ||||
-rw-r--r-- | mesh_snap_utilities_line/snap_context_l/__init__.py | 153 |
5 files changed, 583 insertions, 337 deletions
diff --git a/mesh_snap_utilities_line/__init__.py b/mesh_snap_utilities_line/__init__.py index 58511339..0ce402b5 100644 --- a/mesh_snap_utilities_line/__init__.py +++ b/mesh_snap_utilities_line/__init__.py @@ -22,37 +22,40 @@ bl_info = { "name": "Snap_Utilities_Line", "author": "Germano Cavalcante", - "version": (5, 8, 30), + "version": (5, 9, 00), "blender": (2, 80, 0), - "location": "View3D > TOOLS > Make Line", + "location": "View3D > TOOLS > Line Tool", "description": "Extends Blender Snap controls", #"wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities", "category": "Mesh"} if "bpy" in locals(): import importlib + importlib.reload(common_classes) importlib.reload(preferences) importlib.reload(ops_line) - importlib.reload(common_classes) else: + from . import common_classes from . import preferences from . import ops_line import bpy from bpy.utils.toolsystem import ToolDef +if not __package__: + __package__ = "mesh_snap_utilities_line" + @ToolDef.from_fn -def tool_make_line(): +def tool_line(): import os def draw_settings(context, layout, tool): - addon_prefs = context.preferences.addons["mesh_snap_utilities_line"].preferences + addon_prefs = context.preferences.addons[__package__].preferences layout.prop(addon_prefs, "incremental") layout.prop(addon_prefs, "increments_grid") - if addon_prefs.increments_grid: - layout.prop(addon_prefs, "relative_scale") layout.prop(addon_prefs, "create_face") - layout.prop(addon_prefs, "outer_verts") + if context.mode == 'EDIT_MESH': + layout.prop(addon_prefs, "outer_verts") #props = tool.operator_properties("mesh.snap_utilities_line") #layout.prop(props, "radius") @@ -64,9 +67,9 @@ def tool_make_line(): "Make Lines\n" "Connect them to split faces" ), - icon=os.path.join(icons_dir, "ops.mesh.make_line"), - #widget="MESH_GGT_mouse_point", - operator="mesh.make_line", + icon=os.path.join(icons_dir, "ops.mesh.snap_utilities_line"), + widget="MESH_GGT_snap_point", + #operator="mesh.snap_utilities_line", keymap="3D View Tool: Edit Mesh, Make Line", draw_settings=draw_settings, ) @@ -75,12 +78,13 @@ def tool_make_line(): # ----------------------------------------------------------------------------- # Tool Registraion -def km_3d_view_tool_make_line(tool_mouse = 'LEFTMOUSE'): +def km_3d_view_snap_tools(tool_mouse = 'LEFTMOUSE'): return [( - "3D View Tool: Edit Mesh, Make Line", + tool_line.keymap[0], {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.make_line", {"type": tool_mouse, "value": 'CLICK'}, None), + ("mesh.snap_utilities_line", {"type": tool_mouse, "value": 'CLICK'}, + {"properties": [("wait_for_input", False)]}), ]}, )] @@ -91,67 +95,75 @@ def get_tool_list(space_type, context_mode): return cls._tools[context_mode] -def register_make_line_tool(): +def register_snap_tools(): tools = get_tool_list('VIEW_3D', 'EDIT_MESH') for index, tool in enumerate(tools, 1): - if isinstance(tool, ToolDef) and tool.text == "Add Cube": + if isinstance(tool, ToolDef) and tool.text == "Measure": break - tools.insert(index, tool_make_line) + tools[:index] += None, tool_line + + del tools keyconfigs = bpy.context.window_manager.keyconfigs kc_defaultconf = keyconfigs.get("blender") kc_addonconf = keyconfigs.get("blender addon") - # TODO: find the user defined tool_mouse. - keyconfig_data = km_3d_view_tool_make_line() + keyconfig_data = km_3d_view_snap_tools() + # TODO: find the user defined tool_mouse. from bl_keymap_utils.io import keyconfig_init_from_data keyconfig_init_from_data(kc_defaultconf, keyconfig_data) keyconfig_init_from_data(kc_addonconf, keyconfig_data) -def unregister_make_line_tool(): +def unregister_snap_tools(): tools = get_tool_list('VIEW_3D', 'EDIT_MESH') - tools.remove(tool_make_line) - km_name, km_args, km_content = km_3d_view_tool_make_line()[0] + index = tools.index(tool_line) - 1 #None + tools.pop(index) + tools.remove(tool_line) + + del tools + del index keyconfigs = bpy.context.window_manager.keyconfigs defaultmap = keyconfigs.get("blender").keymaps addonmap = keyconfigs.get("blender addon").keymaps - addonmap.remove(addonmap.find(km_name, **km_args)) - defaultmap.remove(defaultmap.find(km_name, **km_args)) + for keyconfig_data in km_3d_view_snap_tools(): + km_name, km_args, km_content = keyconfig_data + + addonmap.remove(addonmap.find(km_name, **km_args)) + defaultmap.remove(defaultmap.find(km_name, **km_args)) # ----------------------------------------------------------------------------- # Addon Registraion +classes = ( + preferences.SnapUtilitiesLinePreferences, + ops_line.SnapUtilitiesLine, + common_classes.VIEW3D_OT_rotate_custom_pivot, + common_classes.VIEW3D_OT_zoom_custom_target, + common_classes.SnapPointWidget, + common_classes.SnapPointWidgetGroup, +) + def register(): - bpy.utils.register_class(preferences.SnapUtilitiesLinePreferences) - bpy.utils.register_class(common_classes.VIEW3D_OT_rotate_custom_pivot) - bpy.utils.register_class(common_classes.VIEW3D_OT_zoom_custom_target) - bpy.utils.register_class(ops_line.SnapUtilitiesLine) - #bpy.utils.register_class(common_classes.MousePointWidget) - #bpy.utils.register_class(common_classes.MousePointWidgetGroup) + for cls in classes: + bpy.utils.register_class(cls) - register_make_line_tool() + register_snap_tools() def unregister(): - unregister_make_line_tool() + unregister_snap_tools() - #bpy.utils.unregister_class(common_classes.MousePointWidgetGroup) - #bpy.utils.unregister_class(common_classes.MousePointWidget) - bpy.utils.unregister_class(ops_line.SnapUtilitiesLine) - bpy.utils.unregister_class(common_classes.VIEW3D_OT_zoom_custom_target) - bpy.utils.unregister_class(common_classes.VIEW3D_OT_rotate_custom_pivot) - bpy.utils.unregister_class(preferences.SnapUtilitiesLinePreferences) + for cls in reversed(classes): + bpy.utils.unregister_class(cls) if __name__ == "__main__": - __name__ = "mesh_snap_utilities_line" - __package__ = "mesh_snap_utilities_line" register() diff --git a/mesh_snap_utilities_line/common_classes.py b/mesh_snap_utilities_line/common_classes.py index 1c6d75db..ae763828 100644 --- a/mesh_snap_utilities_line/common_classes.py +++ b/mesh_snap_utilities_line/common_classes.py @@ -18,7 +18,13 @@ import bpy import bgl -from .common_utilities import snap_utilities +from mathutils import Vector + +from .common_utilities import ( + convert_distance, + get_units_info, + snap_utilities, + ) class SnapDrawn(): @@ -52,8 +58,6 @@ class SnapDrawn(): self._program_smooth_col = gpu.shader.from_builtin("3D_SMOOTH_COLOR") self._batch_point = None - self._batch_circle = None - self._batch_vector = None def batch_line_strip_create(self, coords): @@ -171,6 +175,7 @@ class SnapDrawn(): gpu.matrix.pop() def draw_elem(self, snap_obj, bm, elem): + #TODO: Cache coords (because antialiasing) import gpu from bmesh.types import( BMVert, @@ -332,122 +337,67 @@ class CharMap: 'LEFT_ARROW', 'RIGHT_ARROW' } - @staticmethod - def modal(self, context, event): - c = event.ascii - if c: - if c == ",": - c = "." - self.length_entered = self.length_entered[:self.line_pos] + c + self.length_entered[self.line_pos:] - self.line_pos += 1 - if self.length_entered: - if event.type == 'BACK_SPACE': - self.length_entered = self.length_entered[:self.line_pos - 1] + self.length_entered[self.line_pos:] - self.line_pos -= 1 - - elif event.type == 'DEL': - self.length_entered = self.length_entered[:self.line_pos] + self.length_entered[self.line_pos + 1:] - - elif event.type == 'LEFT_ARROW': - self.line_pos = (self.line_pos - 1) % (len(self.length_entered) + 1) - - elif event.type == 'RIGHT_ARROW': - self.line_pos = (self.line_pos + 1) % (len(self.length_entered) + 1) - -g_snap_widget = [None] - -class MousePointWidget(bpy.types.Gizmo): - bl_idname = "VIEW3D_GT_mouse_point" + def __init__(self, context): + scale = context.scene.unit_settings.scale_length + separate_units = context.scene.unit_settings.use_separate + self.unit_system = context.scene.unit_settings.system + self.uinfo = get_units_info(scale, self.unit_system, separate_units) + + self.clear() + + def modal_(self, context, event): + if event.value == 'PRESS': + type = event.type + ascii = event.ascii + if (type in self.type) or (ascii in self.ascii): + if ascii: + pos = self.line_pos + if ascii == ",": + ascii = "." + self.length_entered = self.length_entered[:pos] + ascii + self.length_entered[pos:] + self.line_pos += 1 + + if self.length_entered: + pos = self.line_pos + if type == 'BACK_SPACE': + self.length_entered = self.length_entered[:pos - 1] + self.length_entered[pos:] + self.line_pos -= 1 + + elif type == 'DEL': + self.length_entered = self.length_entered[:pos] + self.length_entered[pos + 1:] + + elif type == 'LEFT_ARROW': + self.line_pos = (pos - 1) % (len(self.length_entered) + 1) + + elif type == 'RIGHT_ARROW': + self.line_pos = (pos + 1) % (len(self.length_entered) + 1) + + try: + self.length_entered_value = bpy.utils.units.to_value( + self.unit_system, 'LENGTH', self.length_entered) + except: # ValueError: + self.length_entered_value = 0.0 #invalid + #self.report({'INFO'}, "Operation not supported yet") + else: + self.length_entered_value = 0.0 - __slots__ = ( - "sctx", - "bm", - "draw_cache", - "geom", - "incremental", - "preferences", - "loc", - "snap_obj", - "snap_to_grid", - "type", - ) + return True - def test_select(self, context, mval): - #print('test_select', mval) - self.snap_obj, prev_loc, self.loc, self.type, self.bm, self.geom, len = snap_utilities( - self.sctx, - None, - mval, - increment=self.incremental - ) - context.area.tag_redraw() return False - def draw(self, context): - if self.bm: - self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom) - self.draw_cache.draw(self.type, self.loc, None, None, None) - - def setup(self): - if not hasattr(self, "sctx"): - global g_snap_widget - g_snap_widget[0] = self - - context = bpy.context - - self.preferences = preferences = context.preferences.addons[__package__].preferences - - #Configure the unit of measure - self.snap_to_grid = preferences.increments_grid - self.incremental = bpy.utils.units.to_value( - context.scene.unit_settings.system, 'LENGTH', str(preferences.incremental)) - - self.draw_cache = SnapDrawn( - preferences.out_color, - preferences.face_color, - preferences.edge_color, - preferences.vert_color, - preferences.center_color, - preferences.perpendicular_color, - preferences.constrain_shift_color, - (*context.preferences.themes[0].user_interface.axis_x, 1.0), - (*context.preferences.themes[0].user_interface.axis_y, 1.0), - (*context.preferences.themes[0].user_interface.axis_z, 1.0) - ) - - #Init Snap Context - from .snap_context_l import SnapContext - from mathutils import Vector - - self.sctx = SnapContext(context.region, context.space_data) - self.sctx.set_pixel_dist(12) - self.sctx.use_clip_planes(True) - - if preferences.outer_verts: - for base in context.visible_bases: - self.sctx.add_obj(base.object, base.object.matrix_world) - - self.sctx.set_snap_mode(True, True, True) - self.bm = None - self.type = 'OUT' - self.loc = Vector() - - def __del__(self): - global g_snap_widget - g_snap_widget[0] = None - + def get_converted_length_str(self, length): + if self.length_entered: + pos = self.line_pos + ret = self.length_entered[:pos] + '|' + self.length_entered[pos:] + else: + ret = convert_distance(length, self.uinfo) -class MousePointWidgetGroup(bpy.types.GizmoGroup): - bl_idname = "MESH_GGT_mouse_point" - bl_label = "Draw Mouse Point" - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D'} + return ret - def setup(self, context): - snap_widget = self.gizmos.new(MousePointWidget.bl_idname) - props = snap_widget.target_set_operator("mesh.make_line") - props.wait_for_input = False + def clear(self): + self.length_entered = '' + self.length_entered_value = 0.0 + self.line_pos = 0 class VIEW3D_OT_rotate_custom_pivot(bpy.types.Operator): @@ -532,3 +482,292 @@ class VIEW3D_OT_zoom_custom_target(bpy.types.Operator): return {'RUNNING_MODAL'} return {'FINISHED'} + + +class SnapUtilities: +# __slots__ = ( +# "sctx", +# "draw_cache", +# "outer_verts", +# "snap_face", +# "snap_to_grid", +# "unit_system", +# "rd", +# "incremental", +# ) + + constrain_keys = { + 'X': Vector((1,0,0)), + 'Y': Vector((0,1,0)), + 'Z': Vector((0,0,1)), + 'RIGHT_SHIFT': 'shift', + 'LEFT_SHIFT': 'shift', + } + + snap_widget = None + snap_widget_refcnt = 0 + constrain = None + + @staticmethod + def set_contrain(context, key): + widget = SnapUtilities.snap_widget + if SnapUtilities.constrain == key: + SnapUtilities.constrain = None + return + + SnapUtilities.constrain = key + + + def visible_objects_and_duplis(self, context): + if self.preferences.outer_verts: + for obj in context.visible_objects: + yield (obj, obj.matrix_world) + + if obj.instance_type == 'COLLECTION': + mat = obj.matrix_world.copy() + for ob in obj.instance_collection.objects: + yield (ob, mat @ ob.matrix_world) + else: + for obj in context.objects_in_mode_unique_data: + yield (obj, obj.matrix_world) + + + def snap_context_init(self, context, snap_edge_and_vert = True): + from .snap_context_l import global_snap_context_get + + #Create Snap Context + self.sctx = global_snap_context_get(context.region, context.space_data) + self.sctx.set_pixel_dist(12) + self.sctx.use_clip_planes(True) + + widget = self.snap_widget + + if widget is not None: + self.preferences = widget.preferences + self.draw_cache = widget.draw_cache + else: + preferences = context.preferences.addons[__package__].preferences + self.preferences = preferences + #Init DrawCache + self.draw_cache = SnapDrawn( + preferences.out_color, + preferences.face_color, + preferences.edge_color, + preferences.vert_color, + preferences.center_color, + preferences.perpendicular_color, + preferences.constrain_shift_color, + tuple(context.preferences.themes[0].user_interface.axis_x) + (1.0,), + tuple(context.preferences.themes[0].user_interface.axis_y) + (1.0,), + tuple(context.preferences.themes[0].user_interface.axis_z) + (1.0,) + ) + + self.snap_vert = self.snap_edge = snap_edge_and_vert + + shading = context.space_data.shading + self.snap_face = not (snap_edge_and_vert and + (shading.show_xray or shading.type == 'WIREFRAME')) + + self.snap_context_update(context) + + #Configure the unit of measure + unit_system = context.scene.unit_settings.system + scale = context.scene.unit_settings.scale_length + scale /= context.space_data.overlay.grid_scale + self.rd = bpy.utils.units.to_value(unit_system, 'LENGTH', str(1 / scale)) + + self.incremental = bpy.utils.units.to_value( + unit_system, 'LENGTH', str(self.preferences.incremental)) + + def snap_context_update(self, context): + self.sctx.set_snap_mode( + self.snap_vert, self.snap_edge, self.snap_face) + + self.sctx.clear_snap_objects() + + for obj, matrix in self.visible_objects_and_duplis(context): + self.sctx.add_obj(obj, matrix) + + widget = self.snap_widget + + if widget: + self.snap_obj = widget.snap_obj + self.bm = widget.bm + self.geom = widget.geom + self.type = widget.type + self.location = widget.location + else: + #init these variables to avoid errors + self.snap_obj = None + self.bm = None + self.geom = None + self.type = 'OUT' + self.location = Vector() + + def snap_to_grid(self): + if self.type == 'OUT' and self.preferences.increments_grid: + loc = self.location / self.rd + self.location = Vector((round(loc.x), + round(loc.y), + round(loc.z))) * self.rd + + def snap_context_free(self): + del self.sctx + + del self.bm + del self.draw_cache + del self.geom + del self.location + del self.rd + del self.snap_face + del self.snap_obj + del self.type + + del self.preferences + + SnapUtilities.constrain = None + + +#def mesh_runtime_batchcache_isdirty(me): +# import ctypes +# batch_cache = ctypes.c_void_p.from_address(me.as_pointer() + 1440) +# if batch_cache: +# return ctypes.c_bool.from_address(batch_cache.value + 549).value +# return False + + +class SnapWidgetCommon: + def draw_point_and_elem(self): + if self.bm: + if self.bm.is_valid and self.geom.is_valid: + self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom) + else: + self.bm = None + self.geom = None + self.sctx.update_all() + + self.draw_cache.draw(self.type, self.location, None, None, None) + + def init_snap_widget(self, context, snap_edge_and_vert = True): + self.snap_context_init(context, snap_edge_and_vert) + self.mode = context.mode + self.wm_operators = context.window_manager.operators + self.last_operator = self.wm_operators[-1] if self.wm_operators else None + self.last_mval = None + + def update_snap(self, context, mval): + if self.last_mval == mval: + return -1 + else: + self.last_mval = mval + + last_operator = self.wm_operators[-1] if self.wm_operators else None + if last_operator != self.last_operator: + if (not last_operator or + last_operator.name not in {'Select', 'Loop Select', '(De)select All'}): + ## Something has changed since the last time. + # Has the mesh been changed? + # In the doubt lets clear the snap context. + self.sctx.update_all() + + self.last_operator = last_operator + + #print('test_select', mval) + space = context.space_data + self.sctx.update_viewport_context(context.region, space) + + shading = space.shading + snap_face = not ((self.snap_vert or self.snap_edge) and + (shading.show_xray or shading.type == 'WIREFRAME')) + + if snap_face != self.snap_face: + self.snap_face = snap_face + self.sctx.set_snap_mode( + self.snap_vert, self.snap_edge, self.snap_face) + + self.snap_obj, prev_loc, self.location, self.type, self.bm, self.geom, len = snap_utilities( + self.sctx, + None, + mval, + increment=self.incremental + ) + + def __del__(self): + from .snap_context_l import global_snap_context_get + sctx = global_snap_context_get(None, None) + if sctx: + sctx.clear_snap_objects() + + +class SnapPointWidget(SnapUtilities, SnapWidgetCommon, bpy.types.Gizmo): + bl_idname = "VIEW3D_GT_snap_point" + + __slots__ = ( + "bm", + "draw_cache", + "geom", + "incremental", + "preferences", + "last_operator", + "location", + "mode", + "snap_edge", + "snap_face", + "snap_vert", + "snap_obj", + "type", + "wm_operators", + ) + + def test_select(self, context, mval): + self.update_snap(context, mval) + self.snap_to_grid() + + context.area.tag_redraw() + return -1 + + def draw(self, context): + self.draw_point_and_elem() + + def setup(self): + self.init_snap_widget(bpy.context) + SnapUtilities.snap_widget = self + + +def context_mode_check(context, widget_group): + workspace = context.workspace + mode = workspace.tools_mode + for tool in workspace.tools: + if (tool.widget == widget_group) and (tool.mode == mode): + break + else: + return False + return True + +class SnapWidgetCommon: + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D'} + + @classmethod + def poll(cls, context): + return context_mode_check(context, cls.bl_idname) +# return context_mode_change( +# context, SnapUtilities.snap_widget, cls.bl_idname) + + def init_tool(self, context, gizmo_name): + self.gizmos.new(gizmo_name) + SnapUtilities.snap_widget_refcnt += 1 + + def __del__(self): + SnapUtilities.snap_widget_refcnt -= 1 + if SnapUtilities.snap_widget_refcnt == 0: + SnapUtilities.snap_widget = None + + +class SnapPointWidgetGroup(SnapWidgetCommon, bpy.types.GizmoGroup): + bl_idname = "MESH_GGT_snap_point" + bl_label = "Draw Snap Point" + + def setup(self, context): + self.init_tool(context, SnapPointWidget.bl_idname) diff --git a/mesh_snap_utilities_line/icons/ops.mesh.make_line.dat b/mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat Binary files differindex fa738db9..b6706ff5 100644 --- a/mesh_snap_utilities_line/icons/ops.mesh.make_line.dat +++ b/mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat diff --git a/mesh_snap_utilities_line/ops_line.py b/mesh_snap_utilities_line/ops_line.py index 69be59a7..ba5b5a1a 100644 --- a/mesh_snap_utilities_line/ops_line.py +++ b/mesh_snap_utilities_line/ops_line.py @@ -27,17 +27,15 @@ from .common_classes import ( SnapDrawn, CharMap, SnapNavigation, - g_snap_widget, #TODO: remove + SnapUtilities, ) from .common_utilities import ( - get_units_info, - convert_distance, snap_utilities, ) if not __package__: - __package__ = "mesh_snap_utilities" + __package__ = "mesh_snap_utilities_line" def get_closest_edge(bm, point, dist): @@ -79,7 +77,7 @@ def get_loose_linked_edges(bmvert): return linked -def draw_line(self, bm_geom, location): +def make_line(self, bm_geom, location): obj = self.main_snap_obj.data[0] bm = self.main_bm split_faces = set() @@ -113,8 +111,9 @@ def draw_line(self, bm_geom, location): edge, vert = bmesh.utils.edge_split(bm_geom, bm_geom.verts[0], ret[1]) update_edit_mesh = True - self.list_verts.append(vert) - self.geom = vert # hack to highlight in the drawing + if self.list_verts == [] or self.list_verts[-1] != vert: + self.list_verts.append(vert) + self.geom = vert # hack to highlight in the drawing # self.list_edges.append(edge) else: # constrain point is near @@ -134,7 +133,6 @@ def draw_line(self, bm_geom, location): edge = bm.edges.get([v1, v2]) if edge: self.list_edges.append(edge) - else: if not v2.link_edges: edge = bm.edges.new([v1, v2]) @@ -206,71 +204,31 @@ def draw_line(self, bm_geom, location): return [obj.matrix_world @ v.co for v in self.list_verts] -class SnapUtilitiesLine(bpy.types.Operator): +class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator): """Make Lines. Connect them to split faces""" - bl_idname = "mesh.make_line" + bl_idname = "mesh.snap_utilities_line" bl_label = "Line Tool" bl_options = {'REGISTER'} wait_for_input: bpy.props.BoolProperty(name="Wait for Input", default=True) - constrain_keys = { - 'X': Vector((1,0,0)), - 'Y': Vector((0,1,0)), - 'Z': Vector((0,0,1)), - 'RIGHT_SHIFT': 'shift', - 'LEFT_SHIFT': 'shift', - } - def _exit(self, context): - del self.main_bm #avoids unpredictable crashs - del self.bm + #avoids unpredictable crashs + del self.main_snap_obj + del self.main_bm del self.list_edges del self.list_verts del self.list_verts_co bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') context.area.header_text_set(None) - - if not self.snap_widget: - self.sctx.free() - del self.draw_cache - else: - self.sctx = None - self.draw_cache = None + self.snap_context_free() #Restore initial state context.tool_settings.mesh_select_mode = self.select_mode context.space_data.overlay.show_face_center = self.show_face_center - def _init_snap_context(self, context): - if self.sctx: - self.sctx.clear_snap_objects() - else: - #Create Snap Context - from .snap_context_l import SnapContext - self.sctx = SnapContext(context.region, context.space_data) - self.sctx.set_pixel_dist(12) - self.sctx.use_clip_planes(True) - - if self.outer_verts: - for base in context.visible_bases: - self.sctx.add_obj(base.object, base.object.matrix_world) - - self.sctx.set_snap_mode(True, True, self.snap_face) - - if self.snap_widget: - self.geom = self.snap_widget.geom - self.type = self.snap_widget.type - self.location = self.snap_widget.loc - if self.snap_widget.snap_obj: - context.view_layer.objects.active = self.snap_widget.snap_obj.data[0] - else: - #init these variables to avoid errors - self.geom = None - self.type = 'OUT' - self.location = Vector() - + def _init_snap_line_context(self, context): self.prevloc = Vector() self.list_verts = [] self.list_edges = [] @@ -278,9 +236,6 @@ class SnapUtilitiesLine(bpy.types.Operator): self.bool_update = False self.vector_constrain = () self.len = 0 - self.length_entered = "" - self.length_entered_value = 0.0 - self.line_pos = 0 active_object = context.active_object mesh = active_object.data @@ -295,22 +250,29 @@ class SnapUtilitiesLine(bpy.types.Operator): context.area.tag_redraw() if event.ctrl and event.type == 'Z' and event.value == 'PRESS': - del self.bm - del self.main_bm - bpy.ops.ed.undo() - bpy.ops.object.mode_set(mode='EDIT') # just to be sure - bpy.ops.mesh.select_all(action='DESELECT') - context.tool_settings.mesh_select_mode = (True, False, True) - context.space_data.overlay.show_face_center = True + if not self.wait_for_input: + self._exit(context) + return {'FINISHED'} + else: + del self.bm + del self.main_bm + self.charmap.clear() - self._init_snap_context(context) - self.sctx.update_all() - return {'RUNNING_MODAL'} + bpy.ops.object.mode_set(mode='EDIT') # just to be sure + bpy.ops.mesh.select_all(action='DESELECT') + context.tool_settings.mesh_select_mode = (True, False, True) + context.space_data.overlay.show_face_center = True + + self.snap_context_update(context) + self._init_snap_line_context(context) + self.sctx.update_all() + + return {'RUNNING_MODAL'} is_making_lines = bool(self.list_verts_co) - if (event.type == 'MOUSEMOVE' or self.bool_update) and self.length_entered_value == 0.0: + if (event.type == 'MOUSEMOVE' or self.bool_update) and self.charmap.length_entered_value == 0.0: if self.rv3d.view_matrix != self.rotMat: self.rotMat = self.rv3d.view_matrix.copy() self.bool_update = True @@ -326,14 +288,9 @@ class SnapUtilitiesLine(bpy.types.Operator): mval, constrain=self.vector_constrain, previous_vert=(self.list_verts[-1] if self.list_verts else None), - increment=self.incremental - ) + increment=self.incremental) - if self.snap_to_grid and self.type == 'OUT': - loc = self.location / self.rd - self.location = Vector((round(loc.x), - round(loc.y), - round(loc.z))) * self.rd + self.snap_to_grid() if is_making_lines and self.keyf8: lloc = self.list_verts_co[-1] @@ -356,24 +313,14 @@ class SnapUtilitiesLine(bpy.types.Operator): #del view_vec, orig, location, ax, ay, az, vec if event.value == 'PRESS': - if is_making_lines and (event.ascii in CharMap.ascii or event.type in CharMap.type): - CharMap.modal(self, context, event) - - if self.length_entered != "": - try: - self.length_entered_value = bpy.utils.units.to_value( - self.unit_system, 'LENGTH', self.length_entered) + if is_making_lines and self.charmap.modal_(context, event): + self.bool_update = self.charmap.length_entered_value == 0.0 - vector = (self.location - self.list_verts_co[-1]).normalized() - self.location = (self.list_verts_co[-1] + (vector * self.length_entered_value)) - del vector - - except: # ValueError: - self.length_entered_value = 0.0 #invalid - self.report({'INFO'}, "Operation not supported yet") - else: - self.length_entered_value = 0.0 - self.bool_update = True + if not self.bool_update: + text_value = self.charmap.length_entered_value + vector = (self.location - self.list_verts_co[-1]).normalized() + self.location = self.list_verts_co[-1] + (vector * text_value) + del vector elif event.type in self.constrain_keys: self.bool_update = True @@ -400,7 +347,7 @@ class SnapUtilitiesLine(bpy.types.Operator): self.vector_constrain = [loc, loc + self.constrain_keys[event.type], event.type] elif event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}: - if event.type == 'LEFTMOUSE' or self.length_entered_value != 0.0: + if event.type == 'LEFTMOUSE' or self.charmap.length_entered_value != 0.0: if not is_making_lines and self.bm: self.main_snap_obj = self.snap_obj self.main_bm = self.bm @@ -415,12 +362,10 @@ class SnapUtilitiesLine(bpy.types.Operator): if self.vector_constrain: geom2 = get_closest_edge(self.main_bm, point, .001) - self.list_verts_co = draw_line(self, geom2, point) + self.list_verts_co = make_line(self, geom2, point) self.vector_constrain = None - self.length_entered = "" - self.length_entered_value = 0.0 - self.line_pos = 0 + self.charmap.clear() else: self._exit(context) return {'FINISHED'} @@ -441,19 +386,11 @@ class SnapUtilitiesLine(bpy.types.Operator): self.list_edges = [] self.list_verts = [] self.list_verts_co = [] - self.length_entered = "" - self.length_entered_value = 0.0 - self.line_pos = 0 + self.charmap.clear() a = "" if is_making_lines: - if self.length_entered: - pos = self.line_pos - a = 'length: ' + self.length_entered[:pos] + '|' + self.length_entered[pos:] - else: - length = self.len - length = convert_distance(length, self.uinfo) - a = 'length: ' + length + a = 'length: ' + self.charmap.get_converted_length_str(self.len) context.area.header_text_set(text = "hit: %.3f %.3f %.3f %s" % (*self.location, a)) @@ -469,6 +406,14 @@ class SnapUtilitiesLine(bpy.types.Operator): def invoke(self, context, event): if context.space_data.type == 'VIEW_3D': + self.snap_context_init(context) + self.intersect = self.preferences.intersect + self.create_face = self.preferences.create_face + self.navigation_ops = SnapNavigation(context, True) + self.charmap = CharMap(context) + + self._init_snap_line_context(context) + # print('name', __name__, __package__) #Store current state @@ -487,57 +432,14 @@ class SnapUtilitiesLine(bpy.types.Operator): #Init event variables self.keyf8 = False - self.snap_face = True - - self.snap_widget = g_snap_widget[0] - - if self.snap_widget is not None: - self.draw_cache = self.snap_widget.draw_cache - self.sctx = self.snap_widget.sctx - - preferences = self.snap_widget.preferences - else: - preferences = context.preferences.addons[__package__].preferences - - #Init DrawCache - self.draw_cache = SnapDrawn( - preferences.out_color, - preferences.face_color, - preferences.edge_color, - preferences.vert_color, - preferences.center_color, - preferences.perpendicular_color, - preferences.constrain_shift_color, - tuple(context.preferences.themes[0].user_interface.axis_x) + (1.0,), - tuple(context.preferences.themes[0].user_interface.axis_y) + (1.0,), - tuple(context.preferences.themes[0].user_interface.axis_z) + (1.0,) - ) - - self.sctx = None - - #Configure the unit of measure - self.unit_system = context.scene.unit_settings.system - scale = context.scene.unit_settings.scale_length - separate_units = context.scene.unit_settings.use_separate - self.uinfo = get_units_info(scale, self.unit_system, separate_units) - scale /= context.space_data.overlay.grid_scale * preferences.relative_scale - self.rd = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(1 / scale)) - - self.intersect = preferences.intersect - self.create_face = preferences.create_face - self.outer_verts = preferences.outer_verts - self.snap_to_grid = preferences.increments_grid - self.incremental = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(preferences.incremental)) - - self.navigation_ops = SnapNavigation(context, True) - - self._init_snap_context(context) #modals context.window_manager.modal_handler_add(self) if not self.wait_for_input: - self.modal(context, event) + mat_inv = context.object.matrix_world.inverted_safe() + point = mat_inv @ self.location + self.list_verts_co = make_line(self, self.geom, point) self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (), 'WINDOW', 'POST_VIEW') diff --git a/mesh_snap_utilities_line/snap_context_l/__init__.py b/mesh_snap_utilities_line/snap_context_l/__init__.py index 0cf5ad62..17e8ff54 100644 --- a/mesh_snap_utilities_line/snap_context_l/__init__.py +++ b/mesh_snap_utilities_line/snap_context_l/__init__.py @@ -28,6 +28,14 @@ FACE = 4 class _Internal: + global_snap_context = None + + @classmethod + def snap_context_free(cls): + if cls.global_snap_context != None: + cls.global_snap_context.free() + del cls + from .mesh_drawing import ( gpu_Indices_enable_state, gpu_Indices_restore_state, @@ -56,8 +64,6 @@ class _SnapObjectData(): class _SnapOffscreen(): bound = None def __init__(self, width, height): - import ctypes - self.freed = False self.is_bound = False @@ -72,14 +78,9 @@ class _SnapOffscreen(): self.cur_viewport = bgl.Buffer(bgl.GL_INT, 4) bgl.glGenRenderbuffers(1, self.buf_depth) - bgl.glBindRenderbuffer(bgl.GL_RENDERBUFFER, self.buf_depth[0]) - bgl.glRenderbufferStorage(bgl.GL_RENDERBUFFER, bgl.GL_DEPTH_COMPONENT, width, height) - bgl.glGenTextures(1, self.buf_color) - bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.buf_color[0]) - NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0)) - bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, width, height, 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL) - del NULL + self._config_textures() + bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST) bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST) @@ -87,7 +88,10 @@ class _SnapOffscreen(): bgl.glGenFramebuffers(1, self.fbo) bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.fbo[0]) - bgl.glFramebufferRenderbuffer(bgl.GL_FRAMEBUFFER, bgl.GL_DEPTH_ATTACHMENT, bgl.GL_RENDERBUFFER, self.buf_depth[0]) + bgl.glFramebufferRenderbuffer( + bgl.GL_FRAMEBUFFER, bgl.GL_DEPTH_ATTACHMENT, + bgl.GL_RENDERBUFFER, self.buf_depth[0]) + bgl.glFramebufferTexture(bgl.GL_FRAMEBUFFER, bgl.GL_COLOR_ATTACHMENT0, self.buf_color[0], 0) bgl.glDrawBuffers(1, bgl.Buffer(bgl.GL_INT, 1, [bgl.GL_COLOR_ATTACHMENT0])) @@ -98,6 +102,21 @@ class _SnapOffscreen(): bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.cur_fbo[0]) + def _config_textures(self): + import ctypes + + bgl.glBindRenderbuffer(bgl.GL_RENDERBUFFER, self.buf_depth[0]) + bgl.glRenderbufferStorage( + bgl.GL_RENDERBUFFER, bgl.GL_DEPTH_COMPONENT, self.width, self.height) + + NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0)) + bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.buf_color[0]) + bgl.glTexImage2D( + bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, self.width, self.height, + 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL) + del NULL + + def bind(self): if self is not _SnapOffscreen.bound: if _SnapOffscreen.bound is None: @@ -130,6 +149,19 @@ class _SnapOffscreen(): if not is_bound: self.unbind() + def resize(self, width, height): + is_bound = self is _SnapOffscreen.bound + if not is_bound: + self.bind() + + self.width = int(width) + self.height = int(height) + self._config_textures() + + if not is_bound: + self.unbind() + + def __del__(self): if not self.freed: bgl.glDeleteFramebuffers(1, self.fbo) @@ -188,6 +220,7 @@ class SnapContext(): self._offscreen.clear() + ## PRIVATE ## def _get_snap_obj_by_index(self, index): @@ -206,8 +239,8 @@ class SnapContext(): d = 1 m = self.threshold max_val = 2 * m - 1 - last_value = -1 find_next_index = self._snap_mode & FACE and self._snap_mode & (VERT | EDGE) + last_value = -1 if find_next_index else 0 while m < max_val: for i in range(2): while 2 * loc[i] * d < m: @@ -218,7 +251,12 @@ class SnapContext(): if find_next_index: last_value = value r_snap_obj = self._get_snap_obj_by_index(value) - if (r_snap_obj is None) or value < (r_snap_obj.data[1].first_index + len(r_snap_obj.data[1].tri_verts)): + if r_snap_obj is not None: + snap_data = r_snap_obj.data[1] + if value < (snap_data.first_index + len(snap_data.tri_verts)): + # snap to a triangle + continue + else: continue find_next_index = False elif (r_snap_obj is None) or\ @@ -290,6 +328,8 @@ class SnapContext(): for snap_obj in self.snap_objects: if len(snap_obj.data) == 2: snap_obj.data[1].free() + snap_obj.data.pop(1) + del snap_obj.data del snap_obj.mat del snap_obj @@ -297,11 +337,37 @@ class SnapContext(): ## PUBLIC ## + def update_viewport_context(self, region, space): + rv3d = space.region_3d + + if region == self.region and rv3d == self.rv3d: + return + + self.region = region + self.rv3d = rv3d + + if self.rv3d.is_perspective: + self.depth_range = Vector((space.clip_start, space.clip_end)) + else: + self.depth_range = Vector((-space.clip_end, space.clip_end)) + + self.winsize = Vector((self.region.width, self.region.height)) + self._offscreen.resize(*self.winsize) + def clear_snap_objects(self): + self.update_all() self.snap_objects.clear() _Internal.gpu_Indices_mesh_cache_clear() def update_all(self): + for snap_obj in self.snap_objects: + if len(snap_obj.data) == 2: + snap_obj.data[1].free() + snap_obj.data.pop(1) + + self.update_drawing() + + def update_drawing(self): self.drawn_count = 0 self._offset_cur = 1 self._offscreen.clear() @@ -309,23 +375,27 @@ class SnapContext(): def tag_update_drawn_snap_object(self, snap_obj): if len(snap_obj.data) > 1: snap_obj.data[1].free() - del snap_obj.data[1:] - #self.update_all() + snap_obj.data.pop(1) + #self.update_drawing() # Update on next snap_get call # self.proj_mat = None def update_drawn_snap_object(self, snap_obj): - if len(snap_obj.data) > 1: - _Internal.gpu_Indices_enable_state() + _Internal.gpu_Indices_enable_state() - from .mesh_drawing import GPU_Indices_Mesh - snap_vert = self._snap_mode & VERT != 0 - snap_edge = self._snap_mode & EDGE != 0 - snap_face = self._snap_mode & FACE != 0 + from .mesh_drawing import GPU_Indices_Mesh + snap_vert = self._snap_mode & VERT != 0 + snap_edge = self._snap_mode & EDGE != 0 + snap_face = self._snap_mode & FACE != 0 + + if len(snap_obj.data) > 1: snap_obj.data[1].free() - snap_obj.data[1] = GPU_Indices_Mesh(snap_obj.data[0], snap_face, snap_edge, snap_vert) + snap_obj.data.pop(1) + + data = GPU_Indices_Mesh(snap_obj.data[0], snap_face, snap_edge, snap_vert) + snap_obj.data.append(data) - _Internal.gpu_Indices_restore_state() + _Internal.gpu_Indices_restore_state() def use_clip_planes(self, value): _Internal.gpu_Indices_use_clip_planes(self.rv3d, value) @@ -350,12 +420,7 @@ class SnapContext(): self.update_all() def add_obj(self, obj, matrix): - matrix = matrix.copy() - snap_obj = self._get_snap_obj_by_obj(obj) - if not snap_obj: - self.snap_objects.append(_SnapObjectData([obj], matrix)) - else: - self.snap_objects.append(_SnapObjectData(snap_obj.data, matrix)) + self.snap_objects.append(_SnapObjectData([obj], matrix.copy())) return self.snap_objects[-1] @@ -382,7 +447,7 @@ class SnapContext(): if self.proj_mat != proj_mat: self.proj_mat = proj_mat _Internal.gpu_Indices_set_ProjectionMatrix(self.proj_mat) - self.update_all() + self.update_drawing() ray_dir, ray_orig = self.get_ray(mval) for i, snap_obj in enumerate(self.snap_objects[self.drawn_count:], self.drawn_count): @@ -410,7 +475,12 @@ class SnapContext(): if in_threshold: if len(snap_obj.data) == 1: from .mesh_drawing import GPU_Indices_Mesh - snap_obj.data.append(GPU_Indices_Mesh(obj, snap_face, snap_edge, snap_vert)) + is_bound = obj.display_type == 'BOUNDS' + draw_face = snap_face and not is_bound and obj.display_type != 'WIRE' + draw_edge = snap_edge and not is_bound + draw_vert = snap_vert and not is_bound + snap_obj.data.append(GPU_Indices_Mesh(obj, draw_face, draw_edge, draw_vert)) + snap_obj.data[1].set_draw_mode(snap_face, snap_edge, snap_vert) snap_obj.data[1].set_ModelViewMatrix(snap_obj.mat) @@ -449,3 +519,26 @@ class SnapContext(): def free(self): self.__del__() self.freed = True + +def global_snap_context_get(region, space): + if _Internal.global_snap_context == None: + if region is None or space is None: + return + + import atexit + + _Internal.global_snap_context = SnapContext(region, space) + + # Make sure we only registered the callback once. + atexit.unregister(_Internal.snap_context_free) + atexit.register(_Internal.snap_context_free) + + elif region is not None: + _Internal.global_snap_context.update_viewport_context(region, space) + + if ((region.width != _Internal.global_snap_context._offscreen.width) or + (region.height != _Internal.global_snap_context._offscreen.height)): + _Internal.global_snap_context.winsize[:] = region.width, region.height + _Internal.global_snap_context._offscreen.resize(region.width, region.height) + + return _Internal.global_snap_context |