add bl info
[blender-addons-contrib.git] / space_view3d_library_hide.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 # Script copyright (C) Campbell Barton
22
23 bl_info = {
24     "name": "Library Hide",
25     "description": "Hide objects within library dupligroups",
26     "author": "Campbell Barton",
27     "version": (1, 0),
28     "blender": (2, 6, 3),
29     "wiki_url": "",
30     "tracker_url":"",
31     "category": "3D View",
32 }
33
34 import bpy
35 from mathutils import Vector
36 from bpy_extras import view3d_utils
37
38 LIB_HIDE_TEXT_ID = "blender_hide_objects.py"
39
40 LIB_HIDE_TEXT_HEADER = """
41 import bpy
42 print("running: %r" % __file__)
43 def hide(name, lib):
44     obj = bpy.data.objects.get((name, lib))
45     if obj is None:
46         print("hide can't find: %r %r" % (name, lib))
47     else:
48         obj.hide = obj.hide_render = True
49
50 """
51
52 def pick_object(context, event, pick_objects, ray_max=10000.0):
53     """Run this function on left mouse, execute the ray cast"""
54     # get the context arguments
55     scene = context.scene
56     region = context.region
57     rv3d = context.region_data
58     coord = event.mouse_region_x, event.mouse_region_y
59
60     # get the ray from the viewport and mouse
61     view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
62     ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
63     ray_target = ray_origin + (view_vector * ray_max)
64
65     scene.cursor_location = ray_target
66
67     def visible_objects_and_duplis():
68         """Loop over (object, matrix) pairs (mesh only)"""
69
70         for obj in context.visible_objects:  # scene.objects:
71             if obj.hide:
72                 continue
73
74             if obj.type == 'MESH':
75                 yield (None, obj, obj.matrix_world.copy())
76
77             if obj.dupli_type != 'NONE':
78                 print("DupliInst: %r" % obj)
79                 obj.dupli_list_create(scene)
80                 # matrix = obj.matrix_world.copy()
81                 for dob in obj.dupli_list:
82                     obj_dupli = dob.object
83                     if not obj_dupli.hide:
84                         # print("Dupli: %r" % obj_dupli)
85                         if obj_dupli.type == 'MESH':
86                             yield (obj, obj_dupli, dob.matrix.copy())
87
88                 obj.dupli_list_clear()
89
90     def obj_ray_cast(obj, matrix):
91         """Wrapper for ray casting that moves the ray into object space"""
92
93         # get the ray relative to the object
94         matrix_inv = matrix.inverted()
95         ray_origin_obj = matrix_inv * ray_origin
96         ray_target_obj = matrix_inv * ray_target
97
98         mesh = obj.data
99         if not mesh.polygons:
100             return None, None, None
101
102         hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
103
104         if face_index == -1:
105             hit, normal, face_index = obj.ray_cast(ray_target_obj, ray_origin_obj)
106
107         if face_index != -1:
108             return hit, normal, face_index
109         else:
110             return None, None, None
111
112     # cast rays and find the closest object
113     best_length_squared = ray_max * ray_max
114     best_obj = None
115     best_obj_parent = None
116
117     for obj_parent, obj, matrix in visible_objects_and_duplis():
118         if obj.type == 'MESH':
119             hit, normal, face_index = obj_ray_cast(obj, matrix)
120             if hit is not None:
121                 length_squared = (hit - ray_origin).length_squared
122                 if length_squared < best_length_squared:
123                     best_length_squared = length_squared
124                     best_obj = obj
125                     best_obj_parent = obj_parent
126
127     # now we have the object under the mouse cursor,
128     # we could do lots of stuff but for the example just select.
129     if best_obj is not None:
130         pick_objects.append((best_obj, best_obj.hide, best_obj.hide_render))
131         best_obj.hide = True
132         best_obj.hide_render = True
133         
134         #if best_obj_parent:
135         #    best_obj_parent.update_tag(refresh={'OBJECT'})
136         #scene.update()
137         return True
138     else:
139         print("found none")
140         return False
141
142
143 def pick_finalize(context, pick_objects):
144     text = bpy.data.texts.get((LIB_HIDE_TEXT_ID, None))
145     if text is None:
146         text = bpy.data.texts.new(LIB_HIDE_TEXT_ID)
147         text.use_module = True
148         is_new = True
149     else:
150         is_new = False
151
152     if is_new:
153         data = []
154         
155         data += LIB_HIDE_TEXT_HEADER.split("\n")
156     else:
157         data = text.as_string().split("\n")
158
159     data.append("# ---")
160     
161     for pick_obj_tuple in pick_objects:
162         
163         pick_obj = pick_obj_tuple[0]
164         
165         pick_obj.hide = True
166         pick_obj.hide_render = True
167
168         line = "hide(%r, %s)" % (pick_obj.name, repr(pick_obj.library.filepath) if pick_obj.library is not None else "None")
169         data.append(line)
170     
171     text.from_string("\n".join(data))
172
173
174 def pick_restore(pick_obj):
175     best_obj, hide, hide_render = pick_obj
176     best_obj.hide = hide
177     best_obj.hide_render = hide_render
178
179
180 class ViewOperatorRayCast(bpy.types.Operator):
181     """Modal object selection with a ray cast"""
182     bl_idname = "view3d.modal_operator_raycast"
183     bl_label = "RayCast View Operator"
184
185     _header_text = "Add: LMB, Undo: BackSpace, Finish: Enter"
186
187     def _update_header(self, context):
188         if self.pick_objects:
189             pick_obj = self.pick_objects[-1][0]
190             info_obj = "%s, %s" % (pick_obj.name, pick_obj.library.filepath if pick_obj.library is not None else "None")
191             info = "%s - added: %s" % (ViewOperatorRayCast._header_text, info_obj)
192         else:
193             info = ViewOperatorRayCast._header_text
194
195         context.area.header_text_set(info)
196
197     def modal(self, context, event):
198         if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
199             # allow navigation
200             return {'PASS_THROUGH'}
201         elif event.type == 'LEFTMOUSE':
202             if event.value == 'RELEASE':
203                 if pick_object(context, event, self.pick_objects):
204                     self._update_header(context)
205                 return {'RUNNING_MODAL'}
206         elif event.type == 'BACK_SPACE':
207             if event.value == 'RELEASE':
208                 if self.pick_objects:
209                     pick_obj = self.pick_objects.pop()
210                     pick_restore(pick_obj)
211                     self._update_header(context)
212
213         elif event.type in {'RET', 'NUMPAD_ENTER'}:
214             if event.value == 'RELEASE':
215                 if self.pick_objects:  # avoid enter taking effect on startup
216                     pick_finalize(context, self.pick_objects)
217                     context.area.header_text_set()
218                     self.report({'INFO'}, "Finished")
219                     return {'FINISHED'}
220                 
221         elif event.type in {'RIGHTMOUSE', 'ESC'}:
222             if event.value == 'RELEASE':
223                 for pick_obj in self.pick_objects:
224                     pick_restore(pick_obj)
225                 context.area.header_text_set()
226                 self.report({'INFO'}, "Cancelled")
227                 return {'CANCELLED'}
228
229         return {'RUNNING_MODAL'}
230
231     def invoke(self, context, event):
232         if context.space_data.type == 'VIEW_3D':
233             
234             self.pick_objects = []
235             self._update_header(context)
236
237             context.window_manager.modal_handler_add(self)
238             return {'RUNNING_MODAL'}
239         else:
240             self.report({'WARNING'}, "Active space must be a View3d")
241             return {'CANCELLED'}
242
243
244 def register():
245     bpy.utils.register_class(ViewOperatorRayCast)
246
247
248 def unregister():
249     bpy.utils.unregister_class(ViewOperatorRayCast)
250
251
252 if __name__ == "__main__":
253     register()