diff options
author | meta-androcto <meta.androcto1@gmail.com> | 2019-05-24 09:11:07 +0300 |
---|---|---|
committer | meta-androcto <meta.androcto1@gmail.com> | 2019-05-24 09:11:07 +0300 |
commit | f7c91d3382ea19ce4565105c85288044a2d1b833 (patch) | |
tree | 485be78a423618202851da52858daa6069a57e98 /object_fracture_cell | |
parent | 9e99e90f08c985cb764ecb3a7bba6ff534d4d874 (diff) |
object_fracture_cell/crackit: move to contrib: T63750 T61901
Diffstat (limited to 'object_fracture_cell')
-rw-r--r-- | object_fracture_cell/__init__.py | 568 | ||||
-rw-r--r-- | object_fracture_cell/fracture_cell_calc.py | 120 | ||||
-rw-r--r-- | object_fracture_cell/fracture_cell_setup.py | 459 |
3 files changed, 0 insertions, 1147 deletions
diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py deleted file mode 100644 index e1fcb41f..00000000 --- a/object_fracture_cell/__init__.py +++ /dev/null @@ -1,568 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -bl_info = { - "name": "Cell Fracture", - "author": "ideasman42, phymec, Sergey Sharybin", - "version": (0, 1), - "blender": (2, 70, 0), - "location": "Edit panel of Tools tab, in Object mode, 3D View tools", - "description": "Fractured Object, Bomb, Projectile, Recorder", - "warning": "", - "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" - "Scripts/Object/CellFracture", - "category": "Object"} - - -#if "bpy" in locals(): -# import importlib -# importlib.reload(fracture_cell_setup) - -import bpy -from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - FloatVectorProperty, - EnumProperty, - ) - -from bpy.types import Operator - -def main_object(context, obj, level, **kw): - import random - - # pull out some args - kw_copy = kw.copy() - use_recenter = kw_copy.pop("use_recenter") - use_remove_original = kw_copy.pop("use_remove_original") - recursion = kw_copy.pop("recursion") - recursion_source_limit = kw_copy.pop("recursion_source_limit") - recursion_clamp = kw_copy.pop("recursion_clamp") - recursion_chance = kw_copy.pop("recursion_chance") - recursion_chance_select = kw_copy.pop("recursion_chance_select") - use_layer_next = kw_copy.pop("use_layer_next") - use_layer_index = kw_copy.pop("use_layer_index") - group_name = kw_copy.pop("group_name") - use_island_split = kw_copy.pop("use_island_split") - use_debug_bool = kw_copy.pop("use_debug_bool") - use_interior_vgroup = kw_copy.pop("use_interior_vgroup") - use_sharp_edges = kw_copy.pop("use_sharp_edges") - use_sharp_edges_apply = kw_copy.pop("use_sharp_edges_apply") - - collection = context.collection - - if level != 0: - kw_copy["source_limit"] = recursion_source_limit - - from . import fracture_cell_setup - - # not essential but selection is visual distraction. - obj.select_set(False) - - if kw_copy["use_debug_redraw"]: - obj_display_type_prev = obj.display_type - obj.display_type = 'WIRE' - - objects = fracture_cell_setup.cell_fracture_objects(context, obj, **kw_copy) - objects = fracture_cell_setup.cell_fracture_boolean(context, obj, objects, - use_island_split=use_island_split, - use_interior_hide=(use_interior_vgroup or use_sharp_edges), - use_debug_bool=use_debug_bool, - use_debug_redraw=kw_copy["use_debug_redraw"], - level=level, - ) - - # must apply after boolean. - if use_recenter: - bpy.ops.object.origin_set({"selected_editable_objects": objects}, - type='ORIGIN_GEOMETRY', center='MEDIAN') - - if level == 0: - for level_sub in range(1, recursion + 1): - - objects_recurse_input = [(i, o) for i, o in enumerate(objects)] - - if recursion_chance != 1.0: - from mathutils import Vector - if recursion_chance_select == 'RANDOM': - random.shuffle(objects_recurse_input) - elif recursion_chance_select in {'SIZE_MIN', 'SIZE_MAX'}: - objects_recurse_input.sort(key=lambda ob_pair: - (Vector(ob_pair[1].bound_box[0]) - - Vector(ob_pair[1].bound_box[6])).length_squared) - if recursion_chance_select == 'SIZE_MAX': - objects_recurse_input.reverse() - elif recursion_chance_select in {'CURSOR_MIN', 'CURSOR_MAX'}: - c = scene.cursor.location.copy() - objects_recurse_input.sort(key=lambda ob_pair: - (ob_pair[1].location - c).length_squared) - if recursion_chance_select == 'CURSOR_MAX': - objects_recurse_input.reverse() - - objects_recurse_input[int(recursion_chance * len(objects_recurse_input)):] = [] - objects_recurse_input.sort() - - # reverse index values so we can remove from original list. - objects_recurse_input.reverse() - - objects_recursive = [] - for i, obj_cell in objects_recurse_input: - assert(objects[i] is obj_cell) - objects_recursive += main_object(context, obj_cell, level_sub, **kw) - if use_remove_original: - collection.objects.unlink(obj_cell) - del objects[i] - if recursion_clamp and len(objects) + len(objects_recursive) >= recursion_clamp: - break - objects.extend(objects_recursive) - - if recursion_clamp and len(objects) > recursion_clamp: - break - - #-------------- - # Level Options - if level == 0: - # import pdb; pdb.set_trace() - if use_interior_vgroup or use_sharp_edges: - fracture_cell_setup.cell_fracture_interior_handle(objects, - use_interior_vgroup=use_interior_vgroup, - use_sharp_edges=use_sharp_edges, - use_sharp_edges_apply=use_sharp_edges_apply, - ) - - #-------------- - # Scene Options - - # layer - layers_new = None - if use_layer_index != 0: - layers_new = [False] * 20 - layers_new[use_layer_index - 1] = True - elif use_layer_next: - layers_new = [False] * 20 - layers_new[(obj.layers[:].index(True) + 1) % 20] = True - - if layers_new is not None: - for obj_cell in objects: - obj_cell.layers = layers_new - - # group - if group_name: - group = bpy.data.collections.get(group_name) - if group is None: - group = bpy.data.collections.new(group_name) - group_objects = group.objects[:] - for obj_cell in objects: - if obj_cell not in group_objects: - group.objects.link(obj_cell) - - if kw_copy["use_debug_redraw"]: - obj.display_type = obj_display_type_prev - - # testing only! - # obj.hide = True - return objects - - -def main(context, **kw): - import time - t = time.time() - objects_context = context.selected_editable_objects - - kw_copy = kw.copy() - - # mass - mass_mode = kw_copy.pop("mass_mode") - mass = kw_copy.pop("mass") - - objects = [] - for obj in objects_context: - if obj.type == 'MESH': - objects += main_object(context, obj, 0, **kw_copy) - - bpy.ops.object.select_all(action='DESELECT') - for obj_cell in objects: - obj_cell.select_set(True) - - if mass_mode == 'UNIFORM': - for obj_cell in objects: - obj_cell.game.mass = mass - elif mass_mode == 'VOLUME': - from mathutils import Vector - def _get_volume(obj_cell): - def _getObjectBBMinMax(): - min_co = Vector((1000000.0, 1000000.0, 1000000.0)) - max_co = -min_co - matrix = obj_cell.matrix_world - for i in range(0, 8): - bb_vec = obj_cell.matrix_world * Vector(obj_cell.bound_box[i]) - min_co[0] = min(bb_vec[0], min_co[0]) - min_co[1] = min(bb_vec[1], min_co[1]) - min_co[2] = min(bb_vec[2], min_co[2]) - max_co[0] = max(bb_vec[0], max_co[0]) - max_co[1] = max(bb_vec[1], max_co[1]) - max_co[2] = max(bb_vec[2], max_co[2]) - return (min_co, max_co) - - def _getObjectVolume(): - min_co, max_co = _getObjectBBMinMax() - x = max_co[0] - min_co[0] - y = max_co[1] - min_co[1] - z = max_co[2] - min_co[2] - volume = x * y * z - return volume - - return _getObjectVolume() - - - obj_volume_ls = [_get_volume(obj_cell) for obj_cell in objects] - obj_volume_tot = sum(obj_volume_ls) - if obj_volume_tot > 0.0: - mass_fac = mass / obj_volume_tot - for i, obj_cell in enumerate(objects): - obj_cell.game.mass = obj_volume_ls[i] * mass_fac - else: - assert(0) - - print("Done! %d objects in %.4f sec" % (len(objects), time.time() - t)) - - -class FractureCell(Operator): - bl_idname = "object.add_fracture_cell_objects" - bl_label = "Cell fracture selected mesh objects" - bl_options = {'PRESET'} - - # ------------------------------------------------------------------------- - # Source Options - source: EnumProperty( - name="Source", - items=(('VERT_OWN', "Own Verts", "Use own vertices"), - ('VERT_CHILD', "Child Verts", "Use child object vertices"), - ('PARTICLE_OWN', "Own Particles", ("All particle systems of the " - "source object")), - ('PARTICLE_CHILD', "Child Particles", ("All particle systems of the " - "child objects")), - ('PENCIL', "Grease Pencil", "This object's grease pencil"), - ), - options={'ENUM_FLAG'}, - default={'PARTICLE_OWN'}, - ) - - source_limit: IntProperty( - name="Source Limit", - description="Limit the number of input points, 0 for unlimited", - min=0, max=5000, - default=100, - ) - - source_noise: FloatProperty( - name="Noise", - description="Randomize point distribution", - min=0.0, max=1.0, - default=0.0, - ) - - cell_scale: FloatVectorProperty( - name="Scale", - description="Scale Cell Shape", - size=3, - min=0.0, max=1.0, - default=(1.0, 1.0, 1.0), - ) - - # ------------------------------------------------------------------------- - # Recursion - - recursion: IntProperty( - name="Recursion", - description="Break shards recursively", - min=0, max=5000, - default=0, - ) - - recursion_source_limit: IntProperty( - name="Source Limit", - description="Limit the number of input points, 0 for unlimited (applies to recursion only)", - min=0, max=5000, - default=8, - ) - - recursion_clamp: IntProperty( - name="Clamp Recursion", - description="Finish recursion when this number of objects is reached (prevents recursing for extended periods of time), zero disables", - min=0, max=10000, - default=250, - ) - - recursion_chance: FloatProperty( - name="Random Factor", - description="Likelihood of recursion", - min=0.0, max=1.0, - default=0.25, - ) - - recursion_chance_select: EnumProperty( - name="Recurse Over", - items=(('RANDOM', "Random", ""), - ('SIZE_MIN', "Small", "Recursively subdivide smaller objects"), - ('SIZE_MAX', "Big", "Recursively subdivide bigger objects"), - ('CURSOR_MIN', "Cursor Close", "Recursively subdivide objects closer to the cursor"), - ('CURSOR_MAX', "Cursor Far", "Recursively subdivide objects farther from the cursor"), - ), - default='SIZE_MIN', - ) - - # ------------------------------------------------------------------------- - # Mesh Data Options - - use_smooth_faces: BoolProperty( - name="Smooth Faces", - default=False, - ) - - use_sharp_edges: BoolProperty( - name="Sharp Edges", - description="Set sharp edges when disabled", - default=True, - ) - - use_sharp_edges_apply: BoolProperty( - name="Apply Split Edge", - description="Split sharp hard edges", - default=True, - ) - - use_data_match: BoolProperty( - name="Match Data", - description="Match original mesh materials and data layers", - default=True, - ) - - use_island_split: BoolProperty( - name="Split Islands", - description="Split disconnected meshes", - default=True, - ) - - margin: FloatProperty( - name="Margin", - description="Gaps for the fracture (gives more stable physics)", - min=0.0, max=1.0, - default=0.001, - ) - - material_index: IntProperty( - name="Material", - description="Material index for interior faces", - default=0, - ) - - use_interior_vgroup: BoolProperty( - name="Interior VGroup", - description="Create a vertex group for interior verts", - default=False, - ) - - # ------------------------------------------------------------------------- - # Physics Options - - mass_mode: EnumProperty( - name="Mass Mode", - items=(('VOLUME', "Volume", "Objects get part of specified mass based on their volume"), - ('UNIFORM', "Uniform", "All objects get the specified mass"), - ), - default='VOLUME', - ) - - mass: FloatProperty( - name="Mass", - description="Mass to give created objects", - min=0.001, max=1000.0, - default=1.0, - ) - - - # ------------------------------------------------------------------------- - # Object Options - - use_recenter: BoolProperty( - name="Recenter", - description="Recalculate the center points after splitting", - default=True, - ) - - use_remove_original: BoolProperty( - name="Remove Original", - description="Removes the parents used to create the shatter", - default=True, - ) - - # ------------------------------------------------------------------------- - # Scene Options - # - # .. different from object options in that this controls how the objects - # are setup in the scene. - - use_layer_index: IntProperty( - name="Layer Index", - description="Layer to add the objects into or 0 for existing", - default=0, - min=0, max=20, - ) - - use_layer_next: BoolProperty( - name="Next Layer", - description="At the object into the next layer (layer index overrides)", - default=True, - ) - - group_name: StringProperty( - name="Group", - description="Create objects int a group " - "(use existing or create new)", - ) - - # ------------------------------------------------------------------------- - # Debug - use_debug_points: BoolProperty( - name="Debug Points", - description="Create mesh data showing the points used for fracture", - default=False, - ) - - use_debug_redraw: BoolProperty( - name="Show Progress Realtime", - description="Redraw as fracture is done", - default=True, - ) - - use_debug_bool: BoolProperty( - name="Debug Boolean", - description="Skip applying the boolean modifier", - default=False, - ) - - def execute(self, context): - keywords = self.as_keywords() # ignore=("blah",) - - main(context, **keywords) - - return {'FINISHED'} - - - def invoke(self, context, event): - print(self.recursion_chance_select) - wm = context.window_manager - return wm.invoke_props_dialog(self, width=600) - - def draw(self, context): - layout = self.layout - box = layout.box() - col = box.column() - col.label(text="Point Source") - rowsub = col.row() - rowsub.prop(self, "source") - rowsub = col.row() - rowsub.prop(self, "source_limit") - rowsub.prop(self, "source_noise") - rowsub = col.row() - rowsub.prop(self, "cell_scale") - - box = layout.box() - col = box.column() - col.label(text="Recursive Shatter") - rowsub = col.row(align=True) - rowsub.prop(self, "recursion") - rowsub.prop(self, "recursion_source_limit") - rowsub.prop(self, "recursion_clamp") - rowsub = col.row() - rowsub.prop(self, "recursion_chance") - rowsub.prop(self, "recursion_chance_select", expand=True) - - box = layout.box() - col = box.column() - col.label(text="Mesh Data") - rowsub = col.row() - rowsub.prop(self, "use_smooth_faces") - rowsub.prop(self, "use_sharp_edges") - rowsub.prop(self, "use_sharp_edges_apply") - rowsub.prop(self, "use_data_match") - rowsub = col.row() - - # on same row for even layout but infact are not all that related - rowsub.prop(self, "material_index") - rowsub.prop(self, "use_interior_vgroup") - - # could be own section, control how we subdiv - rowsub.prop(self, "margin") - rowsub.prop(self, "use_island_split") - - - box = layout.box() - col = box.column() - col.label(text="Physics") - rowsub = col.row(align=True) - rowsub.prop(self, "mass_mode") - rowsub.prop(self, "mass") - - - box = layout.box() - col = box.column() - col.label(text="Object") - rowsub = col.row(align=True) - rowsub.prop(self, "use_recenter") - - - box = layout.box() - col = box.column() - col.label(text="Scene") - rowsub = col.row(align=True) - rowsub.prop(self, "use_layer_index") - rowsub.prop(self, "use_layer_next") - rowsub.prop(self, "group_name") - - box = layout.box() - col = box.column() - col.label(text="Debug") - rowsub = col.row(align=True) - rowsub.prop(self, "use_debug_redraw") - rowsub.prop(self, "use_debug_points") - rowsub.prop(self, "use_debug_bool") - - -def menu_func(self, context): - layout = self.layout - layout.label(text="Cell Fracture:") - layout.operator("object.add_fracture_cell_objects", - text="Cell Fracture") - - -def register(): - bpy.utils.register_class(FractureCell) - bpy.types.VIEW3D_PT_tools_object.append(menu_func) - - -def unregister(): - bpy.utils.unregister_class(FractureCell) - bpy.types.VIEW3D_PT_tools_object.remove(menu_func) - - -if __name__ == "__main__": - register() diff --git a/object_fracture_cell/fracture_cell_calc.py b/object_fracture_cell/fracture_cell_calc.py deleted file mode 100644 index 9e6f0de5..00000000 --- a/object_fracture_cell/fracture_cell_calc.py +++ /dev/null @@ -1,120 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Script copyright (C) Blender Foundation 2012 - - -def points_as_bmesh_cells(verts, - points, - points_scale=None, - margin_bounds=0.05, - margin_cell=0.0): - from math import sqrt - import mathutils - from mathutils import Vector - - cells = [] - - points_sorted_current = [p for p in points] - plane_indices = [] - vertices = [] - - if points_scale is not None: - points_scale = tuple(points_scale) - if points_scale == (1.0, 1.0, 1.0): - points_scale = None - - # there are many ways we could get planes - convex hull for eg - # but it ends up fastest if we just use bounding box - if 1: - xa = [v[0] for v in verts] - ya = [v[1] for v in verts] - za = [v[2] for v in verts] - - xmin, xmax = min(xa) - margin_bounds, max(xa) + margin_bounds - ymin, ymax = min(ya) - margin_bounds, max(ya) + margin_bounds - zmin, zmax = min(za) - margin_bounds, max(za) + margin_bounds - convexPlanes = [ - Vector((+1.0, 0.0, 0.0, -xmax)), - Vector((-1.0, 0.0, 0.0, +xmin)), - Vector((0.0, +1.0, 0.0, -ymax)), - Vector((0.0, -1.0, 0.0, +ymin)), - Vector((0.0, 0.0, +1.0, -zmax)), - Vector((0.0, 0.0, -1.0, +zmin)), - ] - - for i, point_cell_current in enumerate(points): - planes = [None] * len(convexPlanes) - for j in range(len(convexPlanes)): - planes[j] = convexPlanes[j].copy() - planes[j][3] += planes[j].xyz.dot(point_cell_current) - distance_max = 10000000000.0 # a big value! - - points_sorted_current.sort(key=lambda p: (p - point_cell_current).length_squared) - - for j in range(1, len(points)): - normal = points_sorted_current[j] - point_cell_current - nlength = normal.length - - if points_scale is not None: - normal_alt = normal.copy() - normal_alt.x *= points_scale[0] - normal_alt.y *= points_scale[1] - normal_alt.z *= points_scale[2] - - # rotate plane to new distance - # should always be positive!! - but abs incase - scalar = normal_alt.normalized().dot(normal.normalized()) - # assert(scalar >= 0.0) - nlength *= scalar - normal = normal_alt - - if nlength > distance_max: - break - - plane = normal.normalized() - plane.resize_4d() - plane[3] = (-nlength / 2.0) + margin_cell - planes.append(plane) - - vertices[:], plane_indices[:] = mathutils.geometry.points_in_planes(planes) - if len(vertices) == 0: - break - - if len(plane_indices) != len(planes): - planes[:] = [planes[k] for k in plane_indices] - - # for comparisons use length_squared and delay - # converting to a real length until the end. - distance_max = 10000000000.0 # a big value! - for v in vertices: - distance = v.length_squared - if distance_max < distance: - distance_max = distance - distance_max = sqrt(distance_max) # make real length - distance_max *= 2.0 - - if len(vertices) == 0: - continue - - cells.append((point_cell_current, vertices[:])) - del vertices[:] - - return cells diff --git a/object_fracture_cell/fracture_cell_setup.py b/object_fracture_cell/fracture_cell_setup.py deleted file mode 100644 index fcafa247..00000000 --- a/object_fracture_cell/fracture_cell_setup.py +++ /dev/null @@ -1,459 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Script copyright (C) Blender Foundation 2012 - -import bpy -import bmesh - - -def _redraw_yasiamevil(): - _redraw_yasiamevil.opr(**_redraw_yasiamevil.arg) -_redraw_yasiamevil.opr = bpy.ops.wm.redraw_timer -_redraw_yasiamevil.arg = dict(type='DRAW_WIN_SWAP', iterations=1) - - -def _points_from_object(obj, source): - - _source_all = { - 'PARTICLE_OWN', 'PARTICLE_CHILD', - 'PENCIL', - 'VERT_OWN', 'VERT_CHILD', - } - - print(source - _source_all) - print(source) - assert(len(source | _source_all) == len(_source_all)) - assert(len(source)) - - points = [] - - def edge_center(mesh, edge): - v1, v2 = edge.vertices - return (mesh.vertices[v1].co + mesh.vertices[v2].co) / 2.0 - - def poly_center(mesh, poly): - from mathutils import Vector - co = Vector() - tot = 0 - for i in poly.loop_indices: - co += mesh.vertices[mesh.loops[i].vertex_index].co - tot += 1 - return co / tot - - def points_from_verts(obj): - """Takes points from _any_ object with geometry""" - if obj.type == 'MESH': - mesh = obj.data - matrix = obj.matrix_world.copy() - points.extend([matrix * v.co for v in mesh.vertices]) - else: - depsgraph = bpy.context.evaluated_depsgraph_get() - ob_eval = ob.evaluated_get(depsgraph) - try: - mesh = ob_eval.to_mesh() - except: - mesh = None - - if mesh is not None: - matrix = obj.matrix_world.copy() - points.extend([matrix * v.co for v in mesh.vertices]) - ob_eval.to_mesh_clear() - - def points_from_particles(obj): - points.extend([p.location.copy() - for psys in obj.particle_systems - for p in psys.particles]) - - # geom own - if 'VERT_OWN' in source: - points_from_verts(obj) - - # geom children - if 'VERT_CHILD' in source: - for obj_child in obj.children: - points_from_verts(obj_child) - - # geom particles - if 'PARTICLE_OWN' in source: - points_from_particles(obj) - - if 'PARTICLE_CHILD' in source: - for obj_child in obj.children: - points_from_particles(obj_child) - - # grease pencil - def get_points(stroke): - return [point.co.copy() for point in stroke.points] - - def get_splines(gp): - if gp.layers.active: - frame = gp.layers.active.active_frame - return [get_points(stroke) for stroke in frame.strokes] - else: - return [] - - if 'PENCIL' in source: - gp = obj.grease_pencil - if gp: - points.extend([p for spline in get_splines(gp) - for p in spline]) - - print("Found %d points" % len(points)) - - return points - - -def cell_fracture_objects(context, obj, - source={'PARTICLE_OWN'}, - source_limit=0, - source_noise=0.0, - clean=True, - # operator options - use_smooth_faces=False, - use_data_match=False, - use_debug_points=False, - margin=0.0, - material_index=0, - use_debug_redraw=False, - cell_scale=(1.0, 1.0, 1.0), - ): - - from . import fracture_cell_calc - collection = context.collection - view_layer = context.view_layer - - # ------------------------------------------------------------------------- - # GET POINTS - - points = _points_from_object(obj, source) - - if not points: - # print using fallback - points = _points_from_object(obj, {'VERT_OWN'}) - - if not points: - print("no points found") - return [] - - # apply optional clamp - if source_limit != 0 and source_limit < len(points): - import random - random.shuffle(points) - points[source_limit:] = [] - - # saddly we cant be sure there are no doubles - from mathutils import Vector - to_tuple = Vector.to_tuple - points = list({to_tuple(p, 4): p for p in points}.values()) - del to_tuple - del Vector - - # end remove doubles - # ------------------ - - if source_noise > 0.0: - from random import random - # boundbox approx of overall scale - from mathutils import Vector - matrix = obj.matrix_world.copy() - bb_world = [matrix * Vector(v) for v in obj.bound_box] - scalar = source_noise * ((bb_world[0] - bb_world[6]).length / 2.0) - - from mathutils.noise import random_unit_vector - - points[:] = [p + (random_unit_vector() * (scalar * random())) for p in points] - - if use_debug_points: - bm = bmesh.new() - for p in points: - bm.verts.new(p) - mesh_tmp = bpy.data.meshes.new(name="DebugPoints") - bm.to_mesh(mesh_tmp) - bm.free() - obj_tmp = bpy.data.objects.new(name=mesh_tmp.name, object_data=mesh_tmp) - collection.objects.link(obj_tmp) - del obj_tmp, mesh_tmp - - mesh = obj.data - matrix = obj.matrix_world.copy() - verts = [matrix * v.co for v in mesh.vertices] - - cells = fracture_cell_calc.points_as_bmesh_cells(verts, - points, - cell_scale, - margin_cell=margin) - - # some hacks here :S - cell_name = obj.name + "_cell" - - objects = [] - - for center_point, cell_points in cells: - - # --------------------------------------------------------------------- - # BMESH - - # create the convex hulls - bm = bmesh.new() - - # WORKAROUND FOR CONVEX HULL BUG/LIMIT - # XXX small noise - import random - def R(): - return (random.random() - 0.5) * 0.001 - # XXX small noise - - for i, co in enumerate(cell_points): - - # XXX small noise - co.x += R() - co.y += R() - co.z += R() - # XXX small noise - - bm_vert = bm.verts.new(co) - - import mathutils - bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) - try: - bmesh.ops.convex_hull(bm, input=bm.verts) - except RuntimeError: - import traceback - traceback.print_exc() - - if clean: - bm.normal_update() - try: - bmesh.ops.dissolve_limit(bm, verts=bm.verts, angle_limit=0.001) - except RuntimeError: - import traceback - traceback.print_exc() - - if use_smooth_faces: - for bm_face in bm.faces: - bm_face.smooth = True - - if material_index != 0: - for bm_face in bm.faces: - bm_face.material_index = material_index - - - # --------------------------------------------------------------------- - # MESH - mesh_dst = bpy.data.meshes.new(name=cell_name) - - bm.to_mesh(mesh_dst) - bm.free() - del bm - - if use_data_match: - # match materials and data layers so boolean displays them - # currently only materials + data layers, could do others... - mesh_src = obj.data - for mat in mesh_src.materials: - mesh_dst.materials.append(mat) - for lay_attr in ("vertex_colors", "uv_textures"): - lay_src = getattr(mesh_src, lay_attr) - lay_dst = getattr(mesh_dst, lay_attr) - for key in lay_src.keys(): - lay_dst.new(name=key) - - # --------------------------------------------------------------------- - # OBJECT - - obj_cell = bpy.data.objects.new(name=cell_name, object_data=mesh_dst) - collection.objects.link(obj_cell) - # scene.objects.active = obj_cell - obj_cell.location = center_point - - objects.append(obj_cell) - - # support for object materials - if use_data_match: - for i in range(len(mesh_dst.materials)): - slot_src = obj.material_slots[i] - slot_dst = obj_cell.material_slots[i] - - slot_dst.link = slot_src.link - slot_dst.material = slot_src.material - - if use_debug_redraw: - view_layer.update() - _redraw_yasiamevil() - - view_layer.update() - - # move this elsewhere... - for obj_cell in objects: - game = obj_cell.game - game.physics_type = 'RIGID_BODY' - game.use_collision_bounds = True - game.collision_bounds_type = 'CONVEX_HULL' - - return objects - - -def cell_fracture_boolean(context, obj, objects, - use_debug_bool=False, - clean=True, - use_island_split=False, - use_interior_hide=False, - use_debug_redraw=False, - level=0, - remove_doubles=True - ): - - objects_boolean = [] - collection = context.collection - scene = context.scene - view_layer = context.view_layer - depsgraph = context.evaluated_depsgraph_get() - - if use_interior_hide and level == 0: - # only set for level 0 - obj.data.polygons.foreach_set("hide", [False] * len(obj.data.polygons)) - - for obj_cell in objects: - mod = obj_cell.modifiers.new(name="Boolean", type='BOOLEAN') - mod.object = obj - mod.operation = 'INTERSECT' - - if not use_debug_bool: - - if use_interior_hide: - obj_cell.data.polygons.foreach_set("hide", [True] * len(obj_cell.data.polygons)) - - obj_cell_eval = obj_cell.evaluated_get(depsgraph) - mesh_new = bpy.data.meshes.new_from_object(obj_cell_eval) - mesh_old = obj_cell.data - obj_cell.data = mesh_new - obj_cell.modifiers.remove(mod) - - # remove if not valid - if not mesh_old.users: - bpy.data.meshes.remove(mesh_old) - if not mesh_new.vertices: - collection.objects.unlink(obj_cell) - if not obj_cell.users: - bpy.data.objects.remove(obj_cell) - obj_cell = None - if not mesh_new.users: - bpy.data.meshes.remove(mesh_new) - mesh_new = None - - # avoid unneeded bmesh re-conversion - if mesh_new is not None: - bm = None - - if clean: - if bm is None: # ok this will always be true for now... - bm = bmesh.new() - bm.from_mesh(mesh_new) - bm.normal_update() - try: - bmesh.ops.dissolve_limit(bm, verts=bm.verts, edges=bm.edges, angle_limit=0.001) - except RuntimeError: - import traceback - traceback.print_exc() - - if remove_doubles: - if bm is None: - bm = bmesh.new() - bm.from_mesh(mesh_new) - bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) - - if bm is not None: - bm.to_mesh(mesh_new) - bm.free() - - del mesh_new - del mesh_old - - if obj_cell is not None: - objects_boolean.append(obj_cell) - - if use_debug_redraw: - _redraw_yasiamevil() - - if (not use_debug_bool) and use_island_split: - # this is ugly and Im not proud of this - campbell - for ob in view_layer.objects: - ob.select_set(True) - for obj_cell in objects_boolean: - obj_cell.select_set(True) - - bpy.ops.mesh.separate(type='LOOSE') - - objects_boolean[:] = [obj_cell for obj_cell in scene.objects if obj_cell.select] - - context.view_layer.update() - - return objects_boolean - - -def cell_fracture_interior_handle(objects, - use_interior_vgroup=False, - use_sharp_edges=False, - use_sharp_edges_apply=False, - ): - """Run after doing _all_ booleans""" - - assert(use_interior_vgroup or use_sharp_edges or use_sharp_edges_apply) - - for obj_cell in objects: - mesh = obj_cell.data - bm = bmesh.new() - bm.from_mesh(mesh) - - if use_interior_vgroup: - for bm_vert in bm.verts: - bm_vert.tag = True - for bm_face in bm.faces: - if not bm_face.hide: - for bm_vert in bm_face.verts: - bm_vert.tag = False - - # now add all vgroups - defvert_lay = bm.verts.layers.deform.verify() - for bm_vert in bm.verts: - if bm_vert.tag: - bm_vert[defvert_lay][0] = 1.0 - - # add a vgroup - obj_cell.vertex_groups.new(name="Interior") - - if use_sharp_edges: - mesh.show_edge_sharp = True - for bm_edge in bm.edges: - if len({bm_face.hide for bm_face in bm_edge.link_faces}) == 2: - bm_edge.smooth = False - - if use_sharp_edges_apply: - edges = [edge for edge in bm.edges if edge.smooth is False] - if edges: - bm.normal_update() - bmesh.ops.split_edges(bm, edges=edges) - - for bm_face in bm.faces: - bm_face.hide = False - - bm.to_mesh(mesh) - bm.free() |