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 #####
21 # Script copyright (C) Blender Foundation 2012
27 def _redraw_yasiamevil():
28 _redraw_yasiamevil
.opr(**_redraw_yasiamevil
.arg
)
29 _redraw_yasiamevil
.opr
= bpy
.ops
.wm
.redraw_timer
30 _redraw_yasiamevil
.arg
= dict(type='DRAW_WIN_SWAP', iterations
=1)
33 def _points_from_object(obj
, source
):
36 'PARTICLE_OWN', 'PARTICLE_CHILD',
38 'VERT_OWN', 'VERT_CHILD',
41 print(source
- _source_all
)
43 assert(len(source | _source_all
) == len(_source_all
))
48 def edge_center(mesh
, edge
):
49 v1
, v2
= edge
.vertices
50 return (mesh
.vertices
[v1
].co
+ mesh
.vertices
[v2
].co
) / 2.0
52 def poly_center(mesh
, poly
):
53 from mathutils
import Vector
56 for i
in poly
.loop_indices
:
57 co
+= mesh
.vertices
[mesh
.loops
[i
].vertex_index
].co
61 def points_from_verts(obj
):
62 """Takes points from _any_ object with geometry"""
63 if obj
.type == 'MESH':
65 matrix
= obj
.matrix_world
.copy()
66 points
.extend([matrix
* v
.co
for v
in mesh
.vertices
])
69 mesh
= ob
.to_mesh(scene
=bpy
.context
.scene
,
76 matrix
= obj
.matrix_world
.copy()
77 points
.extend([matrix
* v
.co
for v
in mesh
.vertices
])
78 bpy
.data
.meshes
.remove(mesh
)
80 def points_from_particles(obj
):
81 points
.extend([p
.location
.copy()
82 for psys
in obj
.particle_systems
83 for p
in psys
.particles
])
86 if 'VERT_OWN' in source
:
87 points_from_verts(obj
)
90 if 'VERT_CHILD' in source
:
91 for obj_child
in obj
.children
:
92 points_from_verts(obj_child
)
95 if 'PARTICLE_OWN' in source
:
96 points_from_particles(obj
)
98 if 'PARTICLE_CHILD' in source
:
99 for obj_child
in obj
.children
:
100 points_from_particles(obj_child
)
103 def get_points(stroke
):
104 return [point
.co
.copy() for point
in stroke
.points
]
108 frame
= gp
.layers
.active
.active_frame
109 return [get_points(stroke
) for stroke
in frame
.strokes
]
113 if 'PENCIL' in source
:
114 gp
= obj
.grease_pencil
116 points
.extend([p
for spline
in get_splines(gp
)
119 print("Found %d points" % len(points
))
124 def cell_fracture_objects(scene
, obj
,
125 source
={'PARTICLE_OWN'},
130 use_smooth_faces
=False,
131 use_data_match
=False,
132 use_debug_points
=False,
135 use_debug_redraw
=False,
136 cell_scale
=(1.0, 1.0, 1.0),
139 from . import fracture_cell_calc
141 # -------------------------------------------------------------------------
144 points
= _points_from_object(obj
, source
)
147 # print using fallback
148 points
= _points_from_object(obj
, {'VERT_OWN'})
151 print("no points found")
154 # apply optional clamp
155 if source_limit
!= 0 and source_limit
< len(points
):
157 random
.shuffle(points
)
158 points
[source_limit
:] = []
160 # saddly we cant be sure there are no doubles
161 from mathutils
import Vector
162 to_tuple
= Vector
.to_tuple
163 points
= list({to_tuple(p
, 4): p
for p
in points
}.values())
170 if source_noise
> 0.0:
171 from random
import random
172 # boundbox approx of overall scale
173 from mathutils
import Vector
174 matrix
= obj
.matrix_world
.copy()
175 bb_world
= [matrix
* Vector(v
) for v
in obj
.bound_box
]
176 scalar
= source_noise
* ((bb_world
[0] - bb_world
[6]).length
/ 2.0)
178 from mathutils
.noise
import random_unit_vector
180 points
[:] = [p
+ (random_unit_vector() * (scalar
* random())) for p
in points
]
186 mesh_tmp
= bpy
.data
.meshes
.new(name
="DebugPoints")
189 obj_tmp
= bpy
.data
.objects
.new(name
=mesh_tmp
.name
, object_data
=mesh_tmp
)
190 scene
.objects
.link(obj_tmp
)
191 del obj_tmp
, mesh_tmp
194 matrix
= obj
.matrix_world
.copy()
195 verts
= [matrix
* v
.co
for v
in mesh
.vertices
]
197 cells
= fracture_cell_calc
.points_as_bmesh_cells(verts
,
203 cell_name
= obj
.name
+ "_cell"
207 for center_point
, cell_points
in cells
:
209 # ---------------------------------------------------------------------
212 # create the convex hulls
215 # WORKAROUND FOR CONVEX HULL BUG/LIMIT
219 return (random
.random() - 0.5) * 0.001
222 for i
, co
in enumerate(cell_points
):
230 bm_vert
= bm
.verts
.new(co
)
233 bmesh
.ops
.remove_doubles(bm
, verts
=bm
.verts
, dist
=0.005)
235 bmesh
.ops
.convex_hull(bm
, input=bm
.verts
)
238 traceback
.print_exc()
243 bmesh
.ops
.dissolve_limit(bm
, verts
=bm
.verts
, angle_limit
=0.001)
246 traceback
.print_exc()
249 for bm_face
in bm
.faces
:
250 bm_face
.smooth
= True
252 if material_index
!= 0:
253 for bm_face
in bm
.faces
:
254 bm_face
.material_index
= material_index
257 # ---------------------------------------------------------------------
259 mesh_dst
= bpy
.data
.meshes
.new(name
=cell_name
)
266 # match materials and data layers so boolean displays them
267 # currently only materials + data layers, could do others...
269 for mat
in mesh_src
.materials
:
270 mesh_dst
.materials
.append(mat
)
271 for lay_attr
in ("vertex_colors", "uv_textures"):
272 lay_src
= getattr(mesh_src
, lay_attr
)
273 lay_dst
= getattr(mesh_dst
, lay_attr
)
274 for key
in lay_src
.keys():
275 lay_dst
.new(name
=key
)
277 # ---------------------------------------------------------------------
280 obj_cell
= bpy
.data
.objects
.new(name
=cell_name
, object_data
=mesh_dst
)
281 scene
.objects
.link(obj_cell
)
282 # scene.objects.active = obj_cell
283 obj_cell
.location
= center_point
285 objects
.append(obj_cell
)
287 # support for object materials
289 for i
in range(len(mesh_dst
.materials
)):
290 slot_src
= obj
.material_slots
[i
]
291 slot_dst
= obj_cell
.material_slots
[i
]
293 slot_dst
.link
= slot_src
.link
294 slot_dst
.material
= slot_src
.material
302 # move this elsewhere...
303 for obj_cell
in objects
:
305 game
.physics_type
= 'RIGID_BODY'
306 game
.use_collision_bounds
= True
307 game
.collision_bounds_type
= 'CONVEX_HULL'
312 def cell_fracture_boolean(scene
, obj
, objects
,
313 use_debug_bool
=False,
315 use_island_split
=False,
316 use_interior_hide
=False,
317 use_debug_redraw
=False,
324 if use_interior_hide
and level
== 0:
325 # only set for level 0
326 obj
.data
.polygons
.foreach_set("hide", [False] * len(obj
.data
.polygons
))
328 for obj_cell
in objects
:
329 mod
= obj_cell
.modifiers
.new(name
="Boolean", type='BOOLEAN')
331 mod
.operation
= 'INTERSECT'
333 if not use_debug_bool
:
335 if use_interior_hide
:
336 obj_cell
.data
.polygons
.foreach_set("hide", [True] * len(obj_cell
.data
.polygons
))
338 mesh_new
= obj_cell
.to_mesh(scene
,
339 apply_modifiers
=True,
341 mesh_old
= obj_cell
.data
342 obj_cell
.data
= mesh_new
343 obj_cell
.modifiers
.remove(mod
)
345 # remove if not valid
346 if not mesh_old
.users
:
347 bpy
.data
.meshes
.remove(mesh_old
)
348 if not mesh_new
.vertices
:
349 scene
.objects
.unlink(obj_cell
)
350 if not obj_cell
.users
:
351 bpy
.data
.objects
.remove(obj_cell
)
353 if not mesh_new
.users
:
354 bpy
.data
.meshes
.remove(mesh_new
)
357 # avoid unneeded bmesh re-conversion
358 if mesh_new
is not None:
362 if bm
is None: # ok this will always be true for now...
364 bm
.from_mesh(mesh_new
)
367 bmesh
.ops
.dissolve_limit(bm
, verts
=bm
.verts
, edges
=bm
.edges
, angle_limit
=0.001)
370 traceback
.print_exc()
375 bm
.from_mesh(mesh_new
)
376 bmesh
.ops
.remove_doubles(bm
, verts
=bm
.verts
, dist
=0.005)
385 if obj_cell
is not None:
386 objects_boolean
.append(obj_cell
)
391 if (not use_debug_bool
) and use_island_split
:
392 # this is ugly and Im not proud of this - campbell
394 for base
in scene
.object_bases
:
396 for obj_cell
in objects_boolean
:
397 obj_cell
.select
= True
399 bpy
.ops
.mesh
.separate(type='LOOSE')
401 objects_boolean
[:] = [obj_cell
for obj_cell
in scene
.objects
if obj_cell
.select
]
405 return objects_boolean
408 def cell_fracture_interior_handle(objects
,
409 use_interior_vgroup
=False,
410 use_sharp_edges
=False,
411 use_sharp_edges_apply
=False,
413 """Run after doing _all_ booleans"""
415 assert(use_interior_vgroup
or use_sharp_edges
or use_sharp_edges_apply
)
417 for obj_cell
in objects
:
422 if use_interior_vgroup
:
423 for bm_vert
in bm
.verts
:
425 for bm_face
in bm
.faces
:
427 for bm_vert
in bm_face
.verts
:
430 # now add all vgroups
431 defvert_lay
= bm
.verts
.layers
.deform
.verify()
432 for bm_vert
in bm
.verts
:
434 bm_vert
[defvert_lay
][0] = 1.0
437 obj_cell
.vertex_groups
.new(name
="Interior")
440 mesh
.show_edge_sharp
= True
441 for bm_edge
in bm
.edges
:
442 if len({bm_face
.hide
for bm_face
in bm_edge
.link_faces
}) == 2:
443 bm_edge
.smooth
= False
445 if use_sharp_edges_apply
:
446 edges
= [edge
for edge
in bm
.edges
if edge
.smooth
is False]
449 bmesh
.ops
.split_edges(bm
, edges
=edges
)
451 for bm_face
in bm
.faces
: