io_mesh_uv_layout: count no longer a keyword only arg
[blender-addons.git] / add_advanced_objects_panels / drop_to_ground.py
blobe6facbcf7208636b0685e33005c7749d75c9ee70
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Drop to Ground1",
21 "author": "Unnikrishnan(kodemax), Florian Meyer(testscreenings)",
22 "blender": (2, 71, 0),
23 "location": "3D View > Toolshelf > Create > Drop To Ground",
24 "description": "Drop selected objects on active object",
25 "warning": "",
26 "category": "Object"}
29 import bpy
30 import bmesh
31 from mathutils import (
32 Vector,
33 Matrix,
35 from bpy.types import (
36 Operator,
37 Panel,
39 from bpy.props import BoolProperty
42 def test_ground_object(ground):
43 if ground.type in {'MESH', 'FONT', 'META', 'CURVE', 'SURFACE'}:
44 return True
45 return False
48 def get_align_matrix(location, normal):
49 up = Vector((0, 0, 1))
50 angle = normal.angle(up)
51 axis = up.cross(normal)
52 mat_rot = Matrix.Rotation(angle, 4, axis)
53 mat_loc = Matrix.Translation(location)
54 mat_align = mat_rot * mat_loc
55 return mat_align
58 def transform_ground_to_world(sc, ground):
59 tmpMesh = ground.to_mesh(sc, True, 'PREVIEW')
60 tmpMesh.transform(ground.matrix_world)
61 tmp_ground = bpy.data.objects.new('tmpGround', tmpMesh)
62 sc.objects.link(tmp_ground)
63 sc.update()
65 return tmp_ground
68 def get_lowest_world_co_from_mesh(ob, mat_parent=None):
69 bme = bmesh.new()
70 bme.from_mesh(ob.data)
71 mat_to_world = ob.matrix_world.copy()
72 if mat_parent:
73 mat_to_world = mat_parent * mat_to_world
74 lowest = None
75 for v in bme.verts:
76 if not lowest:
77 lowest = v
78 if (mat_to_world * v.co).z < (mat_to_world * lowest.co).z:
79 lowest = v
80 lowest_co = mat_to_world * lowest.co
81 bme.free()
83 return lowest_co
86 def get_lowest_world_co(context, ob, mat_parent=None):
87 if ob.type == 'MESH':
88 return get_lowest_world_co_from_mesh(ob)
90 elif ob.type == 'EMPTY' and ob.dupli_type == 'GROUP':
91 if not ob.dupli_group:
92 return None
94 else:
95 lowest_co = None
96 for ob_l in ob.dupli_group.objects:
97 if ob_l.type == 'MESH':
98 lowest_ob_l = get_lowest_world_co_from_mesh(ob_l, ob.matrix_world)
99 if not lowest_co:
100 lowest_co = lowest_ob_l
101 if lowest_ob_l.z < lowest_co.z:
102 lowest_co = lowest_ob_l
104 return lowest_co
107 def drop_objectsall(self, context):
108 ground = context.active_object
109 name = ground.name
111 for obs in bpy.context.scene.objects:
112 obs.select = True
113 if obs.name == name:
114 obs.select = False
116 obs2 = context.selected_objects
118 tmp_ground = transform_ground_to_world(context.scene, ground)
119 down = Vector((0, 0, -10000))
121 for ob in obs2:
122 if self.use_origin:
123 lowest_world_co = ob.location
124 else:
125 lowest_world_co = get_lowest_world_co(context, ob)
127 if not lowest_world_co:
128 message = "Object {} is of type {} works only with Use Center option " \
129 "checked".format(ob.name, ob.type)
130 self.reported.append(message)
131 continue
132 is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
134 if not is_hit:
135 message = ob.name + " did not hit the Ground"
136 self.reported.append(message)
137 continue
139 # simple drop down
140 to_ground_vec = hit_location - lowest_world_co
141 ob.location += to_ground_vec
143 # drop with align to hit normal
144 if self.align:
145 to_center_vec = ob.location - hit_location # vec: hit_loc to origin
146 # rotate object to align with face normal
147 mat_normal = get_align_matrix(hit_location, hit_normal)
148 rot_euler = mat_normal.to_euler()
149 mat_ob_tmp = ob.matrix_world.copy().to_3x3()
150 mat_ob_tmp.rotate(rot_euler)
151 mat_ob_tmp = mat_ob_tmp.to_4x4()
152 ob.matrix_world = mat_ob_tmp
153 # move_object to hit_location
154 ob.location = hit_location
155 # move object above surface again
156 to_center_vec.rotate(rot_euler)
157 ob.location += to_center_vec
159 # cleanup
160 bpy.ops.object.select_all(action='DESELECT')
161 tmp_ground.select = True
162 bpy.ops.object.delete('EXEC_DEFAULT')
163 for ob in obs2:
164 ob.select = True
165 ground.select = True
168 def drop_objects(self, context):
169 ground = context.active_object
171 obs = context.selected_objects
172 if ground in obs:
173 obs.remove(ground)
175 tmp_ground = transform_ground_to_world(context.scene, ground)
176 down = Vector((0, 0, -10000))
178 for ob in obs:
179 if self.use_origin:
180 lowest_world_co = ob.location
181 else:
182 lowest_world_co = get_lowest_world_co(context, ob)
184 if not lowest_world_co:
185 message = "Object {} is of type {} works only with Use Center option " \
186 "checked".format(ob.name, ob.type)
187 self.reported.append(message)
188 continue
190 is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
191 if not is_hit:
192 message = ob.name + " did not hit the Active Object"
193 self.reported.append(message)
194 continue
196 # simple drop down
197 to_ground_vec = hit_location - lowest_world_co
198 ob.location += to_ground_vec
200 # drop with align to hit normal
201 if self.align:
202 to_center_vec = ob.location - hit_location # vec: hit_loc to origin
203 # rotate object to align with face normal
204 mat_normal = get_align_matrix(hit_location, hit_normal)
205 rot_euler = mat_normal.to_euler()
206 mat_ob_tmp = ob.matrix_world.copy().to_3x3()
207 mat_ob_tmp.rotate(rot_euler)
208 mat_ob_tmp = mat_ob_tmp.to_4x4()
209 ob.matrix_world = mat_ob_tmp
210 # move_object to hit_location
211 ob.location = hit_location
212 # move object above surface again
213 to_center_vec.rotate(rot_euler)
214 ob.location += to_center_vec
216 # cleanup
217 bpy.ops.object.select_all(action='DESELECT')
218 tmp_ground.select = True
219 bpy.ops.object.delete('EXEC_DEFAULT')
220 for ob in obs:
221 ob.select = True
222 ground.select = True
225 # define base dummy class for inheritance
226 class DropBaseAtributes:
227 align = BoolProperty(
228 name="Align to ground",
229 description="Aligns the objects' rotation to the ground",
230 default=True)
231 use_origin = BoolProperty(
232 name="Use Origins",
233 description="Drop to objects' origins\n"
234 "Use this option for dropping all types of Objects",
235 default=False)
238 class OBJECT_OT_drop_to_ground(Operator, DropBaseAtributes):
239 bl_idname = "object.drop_on_active"
240 bl_label = "Drop to Ground"
241 bl_description = ("Drop selected objects on the Active object\n"
242 "Active Object has to be of following the types:\n"
243 "Mesh, Font, Metaball, Curve, Surface")
244 bl_options = {'REGISTER', 'UNDO'}
246 reported = []
248 @classmethod
249 def poll(cls, context):
250 act_obj = context.active_object
251 return (len(context.selected_objects) >= 2 and
252 act_obj and test_ground_object(act_obj))
254 def execute(self, context):
255 drop_objects(self, context)
257 if self.reported:
258 self.report({"INFO"},
259 "Some objects could not be dropped (See the Console for more Info)")
260 report_items = " \n".join(self.reported)
261 print("\n[Drop to Ground Report]\n{}\n".format(report_items))
263 self.reported[:] = []
265 return {'FINISHED'}
268 class OBJECT_OT_drop_all_ground(Operator, DropBaseAtributes):
269 bl_idname = "object.drop_all_active"
270 bl_label = "Drop All to Ground (Active Object)"
271 bl_description = ("Drop all other objects onto Active Object\n"
272 "Active Object has to be of following the types:\n"
273 "Mesh, Font, Metaball, Curve, Surface")
274 bl_options = {'REGISTER', 'UNDO'}
276 reported = []
278 @classmethod
279 def poll(cls, context):
280 act_obj = context.active_object
281 return act_obj and test_ground_object(act_obj)
283 def execute(self, context):
284 drop_objectsall(self, context)
286 if self.reported:
287 self.report({"INFO"},
288 "Some objects could not be dropped (See the Console for more Info)")
289 report_items = " \n".join(self.reported)
290 print("\n[Drop All to Ground Report]\n{}\n".format(report_items))
292 self.reported[:] = []
294 return {'FINISHED'}
297 class Drop_help(Operator):
298 bl_idname = "help.drop"
299 bl_label = "Drop to Ground Help"
300 bl_description = "Clik for some information about Drop to Ground"
301 bl_options = {"REGISTER", "INTERNAL"}
303 is_all = BoolProperty(
304 default=True,
305 options={"HIDDEN"}
308 def draw(self, context):
309 layout = self.layout
311 layout.label("General Info:")
312 layout.label("The Active Object has to be of a Mesh, Font,")
313 layout.label("Metaball, Curve or Surface type and")
314 layout.label("be at the lowest Z location")
315 layout.label("The option Use Origins must be enabled to drop")
316 layout.label("objects that are not of a Mesh or DupliGroup type")
317 layout.label("The Active Object has to be big enough to catch them")
318 layout.label("To check that, use the Orthographic Top View")
319 layout.separator()
321 layout.label("To use:")
323 if self.is_all is False:
324 layout.label("Select objects to drop")
325 layout.label("Then Shift Select the object to be the ground")
326 layout.label("Drops Selected Object to the Active one")
327 else:
328 layout.label("Select the ground Mesh and press Drop all")
329 layout.label("The unselected Objects will be moved straight")
330 layout.label("down the Z axis, so they have to be above")
331 layout.label("the Selected / Active one to fall")
333 def execute(self, context):
334 return {'FINISHED'}
336 def invoke(self, context, event):
337 return context.window_manager.invoke_popup(self, width=300)
340 class Drop_Operator_Panel(Panel):
341 bl_label = "Drop To Ground"
342 bl_region_type = "TOOLS"
343 bl_space_type = "VIEW_3D"
344 bl_options = {'DEFAULT_CLOSED'}
345 bl_context = "objectmode"
346 bl_category = "Create"
348 def draw(self, context):
349 layout = self.layout
351 row = layout.split(percentage=0.8, align=True)
352 row.operator(OBJECT_OT_drop_to_ground.bl_idname,
353 text="Drop Selected")
354 row.operator("help.drop", text="", icon="LAYER_USED").is_all = False
356 row = layout.split(percentage=0.8, align=True)
357 row.operator(OBJECT_OT_drop_all_ground.bl_idname,
358 text="Drop All")
359 row.operator("help.drop", text="", icon="LAYER_USED").is_all = True
362 # Register
363 def register():
364 bpy.utils.register_class(OBJECT_OT_drop_all_ground)
365 bpy.utils.register_class(OBJECT_OT_drop_to_ground)
366 bpy.utils.register_class(Drop_Operator_Panel)
367 bpy.utils.register_class(Drop_help)
370 def unregister():
371 bpy.utils.unregister_class(OBJECT_OT_drop_all_ground)
372 bpy.utils.unregister_class(OBJECT_OT_drop_to_ground)
373 bpy.utils.unregister_class(Drop_Operator_Panel)
374 bpy.utils.unregister_class(Drop_help)
377 if __name__ == "__main__":
378 register()