diff --git a/space_view3d_library_hide.py b/space_view3d_library_hide.py new file mode 100644 index 0000000000000000000000000000000000000000..554331f63e934c5f1f9c2b04c90cb74d742da8f7 --- /dev/null +++ b/space_view3d_library_hide.py @@ -0,0 +1,242 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# Script copyright (C) Campbell Barton + +import bpy +from mathutils import Vector +from bpy_extras import view3d_utils + +LIB_HIDE_TEXT_ID = "blender_hide_objects.py" + +LIB_HIDE_TEXT_HEADER = """ +import bpy +print("running: %r" % __file__) +def hide(name, lib): + obj = bpy.data.objects.get((name, lib)) + if obj is None: + print("hide can't find: %r %r" % (name, lib)) + else: + obj.hide = obj.hide_render = True + +""" + +def pick_object(context, event, pick_objects, ray_max=10000.0): + """Run this function on left mouse, execute the ray cast""" + # get the context arguments + scene = context.scene + region = context.region + rv3d = context.region_data + coord = event.mouse_region_x, event.mouse_region_y + + # get the ray from the viewport and mouse + view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) + ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) + ray_target = ray_origin + (view_vector * ray_max) + + scene.cursor_location = ray_target + + def visible_objects_and_duplis(): + """Loop over (object, matrix) pairs (mesh only)""" + + for obj in context.visible_objects: # scene.objects: + if obj.hide: + continue + + if obj.type == 'MESH': + yield (None, obj, obj.matrix_world.copy()) + + if obj.dupli_type != 'NONE': + print("DupliInst: %r" % obj) + obj.dupli_list_create(scene) + # matrix = obj.matrix_world.copy() + for dob in obj.dupli_list: + obj_dupli = dob.object + if not obj_dupli.hide: + # print("Dupli: %r" % obj_dupli) + if obj_dupli.type == 'MESH': + yield (obj, obj_dupli, dob.matrix.copy()) + + obj.dupli_list_clear() + + def obj_ray_cast(obj, matrix): + """Wrapper for ray casting that moves the ray into object space""" + + # get the ray relative to the object + matrix_inv = matrix.inverted() + ray_origin_obj = matrix_inv * ray_origin + ray_target_obj = matrix_inv * ray_target + + mesh = obj.data + if not mesh.polygons: + return None, None, None + + hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj) + + if face_index == -1: + hit, normal, face_index = obj.ray_cast(ray_target_obj, ray_origin_obj) + + if face_index != -1: + return hit, normal, face_index + else: + return None, None, None + + # cast rays and find the closest object + best_length_squared = ray_max * ray_max + best_obj = None + best_obj_parent = None + + for obj_parent, obj, matrix in visible_objects_and_duplis(): + if obj.type == 'MESH': + hit, normal, face_index = obj_ray_cast(obj, matrix) + if hit is not None: + length_squared = (hit - ray_origin).length_squared + if length_squared < best_length_squared: + best_length_squared = length_squared + best_obj = obj + best_obj_parent = obj_parent + + # now we have the object under the mouse cursor, + # we could do lots of stuff but for the example just select. + if best_obj is not None: + pick_objects.append((best_obj, best_obj.hide, best_obj.hide_render)) + best_obj.hide = True + best_obj.hide_render = True + + #if best_obj_parent: + # best_obj_parent.update_tag(refresh={'OBJECT'}) + #scene.update() + return True + else: + print("found none") + return False + + +def pick_finalize(context, pick_objects): + text = bpy.data.texts.get((LIB_HIDE_TEXT_ID, None)) + if text is None: + text = bpy.data.texts.new(LIB_HIDE_TEXT_ID) + text.use_module = True + is_new = True + else: + is_new = False + + if is_new: + data = [] + + data += LIB_HIDE_TEXT_HEADER.split("\n") + else: + data = text.as_string().split("\n") + + data.append("# ---") + + for pick_obj_tuple in pick_objects: + + pick_obj = pick_obj_tuple[0] + + pick_obj.hide = True + pick_obj.hide_render = True + + line = "hide(%r, %s)" % (pick_obj.name, repr(pick_obj.library.filepath) if pick_obj.library is not None else "None") + data.append(line) + + text.from_string("\n".join(data)) + + +def pick_restore(pick_obj): + best_obj, hide, hide_render = pick_obj + best_obj.hide = hide + best_obj.hide_render = hide_render + + +class ViewOperatorRayCast(bpy.types.Operator): + """Modal object selection with a ray cast""" + bl_idname = "view3d.modal_operator_raycast" + bl_label = "RayCast View Operator" + + _header_text = "Add: LMB, Undo: BackSpace, Finish: Enter" + + def _update_header(self, context): + if self.pick_objects: + pick_obj = self.pick_objects[-1][0] + info_obj = "%s, %s" % (pick_obj.name, pick_obj.library.filepath if pick_obj.library is not None else "None") + info = "%s - added: %s" % (ViewOperatorRayCast._header_text, info_obj) + else: + info = ViewOperatorRayCast._header_text + + context.area.header_text_set(info) + + def modal(self, context, event): + if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: + # allow navigation + return {'PASS_THROUGH'} + elif event.type == 'LEFTMOUSE': + if event.value == 'RELEASE': + if pick_object(context, event, self.pick_objects): + self._update_header(context) + return {'RUNNING_MODAL'} + elif event.type == 'BACK_SPACE': + if event.value == 'RELEASE': + if self.pick_objects: + pick_obj = self.pick_objects.pop() + pick_restore(pick_obj) + self._update_header(context) + + elif event.type in {'RET', 'NUMPAD_ENTER'}: + if event.value == 'RELEASE': + if self.pick_objects: # avoid enter taking effect on startup + pick_finalize(context, self.pick_objects) + context.area.header_text_set() + self.report({'INFO'}, "Finished") + return {'FINISHED'} + + elif event.type in {'RIGHTMOUSE', 'ESC'}: + if event.value == 'RELEASE': + for pick_obj in self.pick_objects: + pick_restore(pick_obj) + context.area.header_text_set() + self.report({'INFO'}, "Cancelled") + return {'CANCELLED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + if context.space_data.type == 'VIEW_3D': + + self.pick_objects = [] + self._update_header(context) + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + else: + self.report({'WARNING'}, "Active space must be a View3d") + return {'CANCELLED'} + + +def register(): + bpy.utils.register_class(ViewOperatorRayCast) + + +def unregister(): + bpy.utils.unregister_class(ViewOperatorRayCast) + + +if __name__ == "__main__": + register()