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 #####
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",
31 from mathutils
import (
35 from bpy
.types
import (
39 from bpy
.props
import BoolProperty
42 def test_ground_object(ground
):
43 if ground
.type in {'MESH', 'FONT', 'META', 'CURVE', 'SURFACE'}:
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
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
)
68 def get_lowest_world_co_from_mesh(ob
, mat_parent
=None):
70 bme
.from_mesh(ob
.data
)
71 mat_to_world
= ob
.matrix_world
.copy()
73 mat_to_world
= mat_parent
* mat_to_world
78 if (mat_to_world
* v
.co
).z
< (mat_to_world
* lowest
.co
).z
:
80 lowest_co
= mat_to_world
* lowest
.co
86 def get_lowest_world_co(context
, ob
, mat_parent
=None):
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
:
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
)
100 lowest_co
= lowest_ob_l
101 if lowest_ob_l
.z
< lowest_co
.z
:
102 lowest_co
= lowest_ob_l
107 def drop_objectsall(self
, context
):
108 ground
= context
.active_object
111 for obs
in bpy
.context
.scene
.objects
:
116 obs2
= context
.selected_objects
118 tmp_ground
= transform_ground_to_world(context
.scene
, ground
)
119 down
= Vector((0, 0, -10000))
123 lowest_world_co
= ob
.location
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
)
132 is_hit
, hit_location
, hit_normal
, hit_index
= tmp_ground
.ray_cast(lowest_world_co
, down
)
135 message
= ob
.name
+ " did not hit the Ground"
136 self
.reported
.append(message
)
140 to_ground_vec
= hit_location
- lowest_world_co
141 ob
.location
+= to_ground_vec
143 # drop with align to hit normal
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
160 bpy
.ops
.object.select_all(action
='DESELECT')
161 tmp_ground
.select
= True
162 bpy
.ops
.object.delete('EXEC_DEFAULT')
168 def drop_objects(self
, context
):
169 ground
= context
.active_object
171 obs
= context
.selected_objects
175 tmp_ground
= transform_ground_to_world(context
.scene
, ground
)
176 down
= Vector((0, 0, -10000))
180 lowest_world_co
= ob
.location
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
)
190 is_hit
, hit_location
, hit_normal
, hit_index
= tmp_ground
.ray_cast(lowest_world_co
, down
)
192 message
= ob
.name
+ " did not hit the Active Object"
193 self
.reported
.append(message
)
197 to_ground_vec
= hit_location
- lowest_world_co
198 ob
.location
+= to_ground_vec
200 # drop with align to hit normal
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
217 bpy
.ops
.object.select_all(action
='DESELECT')
218 tmp_ground
.select
= True
219 bpy
.ops
.object.delete('EXEC_DEFAULT')
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",
231 use_origin
= BoolProperty(
233 description
="Drop to objects' origins\n"
234 "Use this option for dropping all types of Objects",
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'}
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
)
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
[:] = []
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'}
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
)
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
[:] = []
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(
308 def draw(self
, context
):
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")
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")
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
):
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
):
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
,
359 row
.operator("help.drop", text
="", icon
="LAYER_USED").is_all
= True
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
)
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__":