1 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Script copyright (C) Blender Foundation 2012
10 if bpy
.app
.background
:
11 def _redraw_yasiamevil():
14 def _redraw_yasiamevil():
15 _redraw_yasiamevil
.opr(**_redraw_yasiamevil
.arg
)
16 _redraw_yasiamevil
.opr
= bpy
.ops
.wm
.redraw_timer
17 _redraw_yasiamevil
.arg
= dict(type='DRAW_WIN_SWAP', iterations
=1)
20 def _points_from_object(depsgraph
, scene
, obj
, source
):
23 'PARTICLE_OWN', 'PARTICLE_CHILD',
25 'VERT_OWN', 'VERT_CHILD',
28 # print(source - _source_all)
30 assert(len(source | _source_all
) == len(_source_all
))
35 def edge_center(mesh
, edge
):
36 v1
, v2
= edge
.vertices
37 return (mesh
.vertices
[v1
].co
+ mesh
.vertices
[v2
].co
) / 2.0
39 def poly_center(mesh
, poly
):
40 from mathutils
import Vector
43 for i
in poly
.loop_indices
:
44 co
+= mesh
.vertices
[mesh
.loops
[i
].vertex_index
].co
48 def points_from_verts(obj
):
49 """Takes points from _any_ object with geometry"""
50 if obj
.type == 'MESH':
52 matrix
= obj
.matrix_world
.copy()
53 points
.extend([matrix
@ v
.co
for v
in mesh
.vertices
])
55 ob_eval
= ob
.evaluated_get(depsgraph
)
57 mesh
= ob_eval
.to_mesh()
62 matrix
= obj
.matrix_world
.copy()
63 points
.extend([matrix
@ v
.co
for v
in mesh
.vertices
])
64 ob_eval
.to_mesh_clear()
66 def points_from_particles(obj
):
67 obj_eval
= obj
.evaluated_get(depsgraph
)
68 points
.extend([p
.location
.copy()
69 for psys
in obj_eval
.particle_systems
70 for p
in psys
.particles
])
73 if 'VERT_OWN' in source
:
74 points_from_verts(obj
)
77 if 'VERT_CHILD' in source
:
78 for obj_child
in obj
.children
:
79 points_from_verts(obj_child
)
82 if 'PARTICLE_OWN' in source
:
83 points_from_particles(obj
)
85 if 'PARTICLE_CHILD' in source
:
86 for obj_child
in obj
.children
:
87 points_from_particles(obj_child
)
90 def get_points(stroke
):
91 return [point
.co
.copy() for point
in stroke
.points
]
95 frame
= gp
.layers
.active
.active_frame
96 return [get_points(stroke
) for stroke
in frame
.strokes
]
100 if 'PENCIL' in source
:
101 # Used to be from object in 2.7x, now from scene.
102 gp
= scene
.grease_pencil
104 points
.extend([p
for spline
in get_splines(gp
) for p
in spline
])
106 print("Found %d points" % len(points
))
111 def cell_fracture_objects(
112 context
, collection
, obj
,
113 source
={'PARTICLE_OWN'},
118 use_smooth_faces
=False,
119 use_data_match
=False,
120 use_debug_points
=False,
123 use_debug_redraw
=False,
124 cell_scale
=(1.0, 1.0, 1.0),
126 from . import fracture_cell_calc
127 depsgraph
= context
.evaluated_depsgraph_get()
128 scene
= context
.scene
129 view_layer
= context
.view_layer
131 # -------------------------------------------------------------------------
134 points
= _points_from_object(depsgraph
, scene
, obj
, source
)
137 # print using fallback
138 points
= _points_from_object(depsgraph
, scene
, obj
, {'VERT_OWN'})
141 print("no points found")
144 # apply optional clamp
145 if source_limit
!= 0 and source_limit
< len(points
):
147 random
.shuffle(points
)
148 points
[source_limit
:] = []
150 # saddly we cant be sure there are no doubles
151 from mathutils
import Vector
152 to_tuple
= Vector
.to_tuple
153 points
= list({to_tuple(p
, 4): p
for p
in points
}.values())
160 if source_noise
> 0.0:
161 from random
import random
162 # boundbox approx of overall scale
163 from mathutils
import Vector
164 matrix
= obj
.matrix_world
.copy()
165 bb_world
= [matrix
@ Vector(v
) for v
in obj
.bound_box
]
166 scalar
= source_noise
* ((bb_world
[0] - bb_world
[6]).length
/ 2.0)
168 from mathutils
.noise
import random_unit_vector
170 points
[:] = [p
+ (random_unit_vector() * (scalar
* random())) for p
in points
]
176 mesh_tmp
= bpy
.data
.meshes
.new(name
="DebugPoints")
179 obj_tmp
= bpy
.data
.objects
.new(name
=mesh_tmp
.name
, object_data
=mesh_tmp
)
180 collection
.objects
.link(obj_tmp
)
181 del obj_tmp
, mesh_tmp
184 matrix
= obj
.matrix_world
.copy()
185 verts
= [matrix
@ v
.co
for v
in mesh
.vertices
]
187 cells
= fracture_cell_calc
.points_as_bmesh_cells(
195 cell_name
= obj
.name
+ "_cell"
199 for center_point
, cell_points
in cells
:
201 # ---------------------------------------------------------------------
204 # create the convex hulls
207 # WORKAROUND FOR CONVEX HULL BUG/LIMIT
212 return (random
.random() - 0.5) * 0.001
215 for i
, co
in enumerate(cell_points
):
223 bm_vert
= bm
.verts
.new(co
)
226 bmesh
.ops
.remove_doubles(bm
, verts
=bm
.verts
, dist
=0.005)
228 bmesh
.ops
.convex_hull(bm
, input=bm
.verts
)
231 traceback
.print_exc()
236 bmesh
.ops
.dissolve_limit(bm
, verts
=bm
.verts
, angle_limit
=0.001)
239 traceback
.print_exc()
240 # Smooth faces will remain only inner faces, after applying boolean modifier.
242 for bm_face
in bm
.faces
:
243 bm_face
.smooth
= True
245 if material_index
!= 0:
246 for bm_face
in bm
.faces
:
247 bm_face
.material_index
= material_index
249 # ---------------------------------------------------------------------
251 mesh_dst
= bpy
.data
.meshes
.new(name
=cell_name
)
258 # match materials and data layers so boolean displays them
259 # currently only materials + data layers, could do others...
261 for mat
in mesh_src
.materials
:
262 mesh_dst
.materials
.append(mat
)
263 for lay_attr
in ("vertex_colors", "uv_layers"):
264 lay_src
= getattr(mesh_src
, lay_attr
)
265 lay_dst
= getattr(mesh_dst
, lay_attr
)
266 for key
in lay_src
.keys():
267 lay_dst
.new(name
=key
)
269 # ---------------------------------------------------------------------
272 obj_cell
= bpy
.data
.objects
.new(name
=cell_name
, object_data
=mesh_dst
)
273 collection
.objects
.link(obj_cell
)
274 # scene.objects.active = obj_cell
275 obj_cell
.location
= center_point
277 objects
.append(obj_cell
)
279 # support for object materials
281 for i
in range(len(mesh_dst
.materials
)):
282 slot_src
= obj
.material_slots
[i
]
283 slot_dst
= obj_cell
.material_slots
[i
]
285 slot_dst
.link
= slot_src
.link
286 slot_dst
.material
= slot_src
.material
297 def cell_fracture_boolean(
298 context
, collection
, obj
, objects
,
299 use_debug_bool
=False,
301 use_island_split
=False,
302 use_interior_hide
=False,
303 use_debug_redraw
=False,
309 scene
= context
.scene
310 view_layer
= context
.view_layer
312 if use_interior_hide
and level
== 0:
313 # only set for level 0
314 obj
.data
.polygons
.foreach_set("hide", [False] * len(obj
.data
.polygons
))
316 for obj_cell
in objects
:
317 mod
= obj_cell
.modifiers
.new(name
="Boolean", type='BOOLEAN')
319 mod
.operation
= 'INTERSECT'
321 if not use_debug_bool
:
323 if use_interior_hide
:
324 obj_cell
.data
.polygons
.foreach_set("hide", [True] * len(obj_cell
.data
.polygons
))
326 # Calculates all booleans at once (faster).
327 depsgraph
= context
.evaluated_depsgraph_get()
329 for obj_cell
in objects
:
331 if not use_debug_bool
:
333 obj_cell_eval
= obj_cell
.evaluated_get(depsgraph
)
334 mesh_new
= bpy
.data
.meshes
.new_from_object(obj_cell_eval
)
335 mesh_old
= obj_cell
.data
336 obj_cell
.data
= mesh_new
337 obj_cell
.modifiers
.remove(obj_cell
.modifiers
[-1])
339 # remove if not valid
340 if not mesh_old
.users
:
341 bpy
.data
.meshes
.remove(mesh_old
)
342 if not mesh_new
.vertices
:
343 collection
.objects
.unlink(obj_cell
)
344 if not obj_cell
.users
:
345 bpy
.data
.objects
.remove(obj_cell
)
347 if not mesh_new
.users
:
348 bpy
.data
.meshes
.remove(mesh_new
)
351 # avoid unneeded bmesh re-conversion
352 if mesh_new
is not None:
356 if bm
is None: # ok this will always be true for now...
358 bm
.from_mesh(mesh_new
)
361 bmesh
.ops
.dissolve_limit(bm
, verts
=bm
.verts
, edges
=bm
.edges
, angle_limit
=0.001)
364 traceback
.print_exc()
369 bm
.from_mesh(mesh_new
)
370 bmesh
.ops
.remove_doubles(bm
, verts
=bm
.verts
, dist
=0.005)
379 if obj_cell
is not None:
380 objects_boolean
.append(obj_cell
)
385 if (not use_debug_bool
) and use_island_split
:
386 # this is ugly and Im not proud of this - campbell
387 for ob
in view_layer
.objects
:
389 for obj_cell
in objects_boolean
:
390 obj_cell
.select_set(True)
392 bpy
.ops
.mesh
.separate(type='LOOSE')
394 objects_boolean
[:] = [obj_cell
for obj_cell
in view_layer
.objects
if obj_cell
.select_get()]
396 context
.view_layer
.update()
398 return objects_boolean
401 def cell_fracture_interior_handle(
403 use_interior_vgroup
=False,
404 use_sharp_edges
=False,
405 use_sharp_edges_apply
=False,
407 """Run after doing _all_ booleans"""
409 assert(use_interior_vgroup
or use_sharp_edges
or use_sharp_edges_apply
)
411 for obj_cell
in objects
:
416 if use_interior_vgroup
:
417 for bm_vert
in bm
.verts
:
419 for bm_face
in bm
.faces
:
421 for bm_vert
in bm_face
.verts
:
424 # now add all vgroups
425 defvert_lay
= bm
.verts
.layers
.deform
.verify()
426 for bm_vert
in bm
.verts
:
428 bm_vert
[defvert_lay
][0] = 1.0
431 obj_cell
.vertex_groups
.new(name
="Interior")
434 for bm_edge
in bm
.edges
:
435 if len({bm_face
.hide
for bm_face
in bm_edge
.link_faces
}) == 2:
436 bm_edge
.smooth
= False
438 if use_sharp_edges_apply
:
439 edges
= [edge
for edge
in bm
.edges
if edge
.smooth
is False]
442 bmesh
.ops
.split_edges(bm
, edges
=edges
)
444 for bm_face
in bm
.faces
: