Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2019-07-01 12:29:56 +0300
committermeta-androcto <meta.androcto1@gmail.com>2019-07-01 12:29:56 +0300
commitfffaf5d2759d38d4166f608eab8871fcd59a7e11 (patch)
treec4a125b1bf06239d66fc078b0168468d6097284e /object_fracture_cell
parentde694451dd015d9eab584e63c18b7269ed282c77 (diff)
object_fracture_cell: return to release: T61901 T63750 f7c91d3382ea
Diffstat (limited to 'object_fracture_cell')
-rw-r--r--object_fracture_cell/__init__.py420
-rw-r--r--object_fracture_cell/operator.py271
-rw-r--r--object_fracture_cell/process/cell_calc.py149
-rw-r--r--object_fracture_cell/process/cell_functions.py599
-rw-r--r--object_fracture_cell/process/cell_main.py208
-rw-r--r--object_fracture_cell/process/crack_functions.py140
-rw-r--r--object_fracture_cell/process/material_functions.py83
-rw-r--r--object_fracture_cell/process/materials/materials1.blendbin0 -> 273382 bytes
-rw-r--r--object_fracture_cell/utilities.py42
9 files changed, 1912 insertions, 0 deletions
diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py
new file mode 100644
index 00000000..d7adc80f
--- /dev/null
+++ b/object_fracture_cell/__init__.py
@@ -0,0 +1,420 @@
+# ##### 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, Nobuyuki Hirakata",
+ "version": (1, 0, 1),
+ "blender": (2, 80, 0),
+ "location": "View3D > Sidebar > Transform tab",
+ "description": "Fractured Object, or Cracked Surface",
+ "warning": "Work in Progress",
+ "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(operator)
+
+else:
+ from . import operator
+
+import bpy
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ IntProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ EnumProperty,
+ BoolVectorProperty,
+ PointerProperty,
+ )
+
+from bpy.types import (
+ Panel,
+ PropertyGroup,
+ )
+
+
+class FRACTURE_PT_Menu(Panel):
+ bl_idname = 'FRACTURE_PT_Menu'
+ bl_label = "Fracture Cell"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "UI"
+ bl_category = "Transform"
+ bl_context = 'objectmode'
+ bl_options = {"DEFAULT_CLOSED"}
+
+ def draw(self, context):
+ # Show pop-upped menu when the button is hit.
+ layout = self.layout
+ #layout.label(text="Cell Fracture:")
+ layout.operator(operator.FRACTURE_OT_Cell.bl_idname,
+ text="1. Cell Fracture")
+ layout.operator(operator.FRACTURE_OT_Crack.bl_idname,
+ text="2. Cell to Crack")
+
+ material_props = context.window_manager.fracture_material_props
+ layout.separator()
+ box = layout.box()
+ col = box.column()
+ row = col.row(align=True)
+ row.label(text="Material Preset:")
+ '''
+ row_sub = row.row()
+ row_sub.prop(material_props, "material_lib_name", text="",
+ toggle=True, icon="LONGDISPLAY")
+ '''
+ row = box.row()
+ row.prop(material_props, "material_preset", text="")
+
+ row = box.row()
+ row.operator(operator.FRACTURE_OT_Material.bl_idname, icon="MATERIAL_DATA",
+ text="Append Material")
+
+
+class FractureCellProperties(PropertyGroup):
+ # -------------------------------------------------------------------------
+ # Source Options
+ source_vert_own: IntProperty(
+ name="Own Verts",
+ description="Use own vertices",
+ min=0, max=2000,
+ default=100,
+ )
+ source_vert_child: IntProperty(
+ name="Child Verts",
+ description="Use child object vertices",
+ min=0, max=2000,
+ default=0,
+ )
+ source_particle_own: IntProperty(
+ name="Own Particles",
+ description="All particle systems of the source object",
+ min=0, max=2000,
+ default=0,
+ )
+ source_particle_child: IntProperty(
+ name="Child Particles",
+ description="All particle systems of the child objects",
+ min=0, max=2000,
+ default=0,
+ )
+ source_pencil: IntProperty(
+ name="Annotation Pencil",
+ description="Annotation Grease Pencil",
+ min=0, max=2000,
+ default=0,
+ )
+ source_random: IntProperty(
+ name="Random",
+ description="Random seed position",
+ min=0, max=2000,
+ default=0,
+ )
+ '''
+ source_limit: IntProperty(
+ name="Source Limit",
+ description="Limit the number of input points, 0 for unlimited",
+ min=0, max=5000,
+ default=100,
+ )
+ '''
+ # -------------------------------------------------------------------------
+ # Transform
+ source_noise: FloatProperty(
+ name="Noise",
+ description="Randomize point distribution",
+ min=0.0, max=1.0,
+ default=0.0,
+ )
+ margin: FloatProperty(
+ name="Margin",
+ description="Gaps for the fracture (gives more stable physics)",
+ min=0.0, max=1.0,
+ default=0.001,
+ )
+ cell_scale: FloatVectorProperty(
+ name="Scale",
+ description="Scale Cell Shape",
+ size=3,
+ min=0.0, max=1.0,
+ default=(1.0, 1.0, 1.0),
+ )
+ pre_simplify : FloatProperty(
+ name="Simplify Base Mesh",
+ description="Simplify base mesh before making cell. Lower face size, faster calculation",
+ default=0.00,
+ min=0.00,
+ max=1.00
+ )
+ use_recenter: BoolProperty(
+ name="Recenter",
+ description="Recalculate the center points after splitting",
+ default=True,
+ )
+ use_island_split: BoolProperty(
+ name="Split Islands",
+ description="Split disconnected meshes",
+ default=True,
+ )
+ # -------------------------------------------------------------------------
+ # Recursion
+ recursion: IntProperty(
+ name="Recursion",
+ description="Break shards recursively",
+ min=0, max=2000,
+ default=0,
+ )
+ recursion_source_limit: IntProperty(
+ name="Fracture Each",
+ description="Limit the number of input points, 0 for unlimited (applies to recursion only)",
+ min=2, max=2000, # Oviously, dividing in more than two objects is needed, to avoid no fracture object.
+ default=8,
+ )
+ recursion_clamp: IntProperty(
+ name="Max Fracture",
+ 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="Recursion Chance",
+ description="Likelihood of recursion",
+ min=0.0, max=1.0,
+ default=1.00,
+ )
+ recursion_chance_select: EnumProperty(
+ name="Target",
+ 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',
+ )
+ # -------------------------------------------------------------------------
+ # Interior Meshes Options
+ use_smooth_faces: BoolProperty(
+ name="Smooth Faces",
+ description="Smooth Faces of inner side",
+ default=False,
+ )
+ use_sharp_edges: BoolProperty(
+ name="Mark Sharp Edges",
+ description="Set sharp edges when disabled",
+ default=False,
+ )
+ use_sharp_edges_apply: BoolProperty(
+ name="Edge Split Modifier",
+ description="Add edge split modofier for sharp edges",
+ default=False,
+ )
+ use_data_match: BoolProperty(
+ name="Copy Original Data",
+ description="Match original mesh materials and data layers",
+ default=True,
+ )
+ material_index: IntProperty(
+ name="Interior Material Slot",
+ description="Material index for interior faces",
+ default=0,
+ )
+ use_interior_vgroup: BoolProperty(
+ name="Vertex Group",
+ description="Create a vertex group for interior verts",
+ default=False,
+ )
+ # -------------------------------------------------------------------------
+ # Scene Options
+ use_collection: BoolProperty(
+ name="Use Collection",
+ description="Use collection to organize fracture objects",
+ default=True,
+ )
+ new_collection: BoolProperty(
+ name="New Collection",
+ description="Make new collection for fracture objects",
+ default=True,
+ )
+ collection_name: StringProperty(
+ name="Name",
+ description="Collection name.",
+ default="Fracture",
+ )
+ original_hide: BoolProperty(
+ name="Hide Original",
+ description="Hide original object after cell fracture.",
+ default=False,
+ )
+ cell_relocate : BoolProperty(
+ name="Move Beside Original",
+ description="Move cells beside the original object.",
+ default=False,
+ )
+ # -------------------------------------------------------------------------
+ # Custom Property Options
+ use_mass: BoolProperty(
+ name="Mass",
+ description="Append mass data on custom properties of cell objects.",
+ default=False,
+ )
+ mass_name: StringProperty(
+ name="Property Name",
+ description="Name for custome properties.",
+ default="mass",
+ )
+ 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 Factor",
+ description="Mass to give created objects",
+ min=0.001, max=1000.0,
+ default=1.0,
+ )
+ # -------------------------------------------------------------------------
+ # 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=False,
+ )
+
+ use_debug_bool: BoolProperty(
+ name="Debug Boolean",
+ description="Skip applying the boolean modifier",
+ default=False,
+ )
+
+
+class FractureCrackProperties(PropertyGroup):
+ modifier_decimate : FloatProperty(
+ name="Reduce Faces",
+ description="Apply Decimate Modifier to reduce face number",
+ default=0.40,
+ min=0.00,
+ max=1.00
+ )
+ modifier_smooth : FloatProperty(
+ name="Loose | Tight",
+ description="Smooth Modifier",
+ default=-0.50,
+ min=-3.00,
+ max=3.00
+ )
+ extrude_scale : FloatProperty(
+ name="Extrude Blob",
+ description="Extrude Scale",
+ default=0.00,
+ min=0.00,
+ max=5.00
+ )
+ extrude_var : FloatProperty(
+ name="Extrude Random ",
+ description="Extrude Varriant",
+ default=0.01,
+ min=-4.00,
+ max=4.00
+ )
+ extrude_num : IntProperty(
+ name="Extrude Num",
+ description="Extrude Number",
+ default=1,
+ min=0,
+ max=10
+ )
+ modifier_wireframe : BoolProperty(
+ name="Wireframe Modifier",
+ description="Wireframe Modifier",
+ default=False
+ )
+
+
+class FractureMaterialProperties(PropertyGroup):
+ # Note: you can choose the original name in the library blend
+ # or the prop name
+ material_preset : EnumProperty(
+ name="Preset",
+ description="Material Preset",
+ items=[
+ ('crackit_organic_mud', "Organic Mud", "Mud material"),
+ ('crackit_mud', "Mud", "Mud material"),
+ ('crackit_tree_moss', "Tree Moss", "Tree Material"),
+ ('crackit_tree_dry', "Tree Dry", "Tree Material"),
+ ('crackit_tree_red', "Tree Red", "Tree Material"),
+ ('crackit_rock', "Rock", "Rock Material"),
+ ('crackit_lava', "Lava", "Lava Material"),
+ ('crackit_wet-paint', "Wet Paint", "Paint Material"),
+ ('crackit_soap', "Soap", "Soap Material"),
+ ]
+ )
+ material_lib_name : BoolProperty(
+ name="Library Name",
+ description="Use the original Material name from the .blend library\n"
+ "instead of the one defined in the Preset",
+ default=True
+ )
+
+classes = (
+ FractureCellProperties,
+ FractureCrackProperties,
+ FractureMaterialProperties,
+ operator.FRACTURE_OT_Cell,
+ operator.FRACTURE_OT_Crack,
+ operator.FRACTURE_OT_Material,
+ FRACTURE_PT_Menu,
+ )
+
+def register():
+ from bpy.utils import register_class
+ for cls in classes:
+ register_class(cls)
+
+ bpy.types.WindowManager.fracture_cell_props = PointerProperty(
+ type=FractureCellProperties
+ )
+ bpy.types.WindowManager.fracture_crack_props = PointerProperty(
+ type=FractureCrackProperties
+ )
+ bpy.types.WindowManager.fracture_material_props = PointerProperty(
+ type=FractureMaterialProperties
+ )
+
+def unregister():
+ del bpy.types.WindowManager.fracture_material_props
+ del bpy.types.WindowManager.fracture_crack_props
+ del bpy.types.WindowManager.fracture_cell_props
+
+ from bpy.utils import unregister_class
+ for cls in reversed(classes):
+ unregister_class(cls) \ No newline at end of file
diff --git a/object_fracture_cell/operator.py b/object_fracture_cell/operator.py
new file mode 100644
index 00000000..cd8dca00
--- /dev/null
+++ b/object_fracture_cell/operator.py
@@ -0,0 +1,271 @@
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(cell_main)
+ importlib.reload(crack_functions)
+ importlib.reload(material_functions)
+ importlib.reload(utilities)
+
+else:
+ from .process import cell_main
+ from .process import crack_functions
+ from .process import material_functions
+ from . import utilities
+
+import bpy
+
+from bpy.types import (
+ Operator,
+ )
+
+class FRACTURE_OT_Cell(Operator):
+ bl_idname = "object.add_fracture_cell"
+ bl_label = "Cell fracture"
+ bl_description = "Make fractured cells from selected object."
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ #keywords = self.as_keywords() # ignore=("blah",)
+
+ fracture_cell_props = context.window_manager.fracture_cell_props
+ cell_keywords = utilities._cell_props_to_dict(fracture_cell_props)
+
+ originals = context.selected_editable_objects
+ for original in originals:
+ cell_main.main(context, original, **cell_keywords)
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self, width=600)
+
+ def draw(self, context):
+ cell_props = context.window_manager.fracture_cell_props
+
+ layout = self.layout
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Fracture From")
+ row = col.row()
+ #row.prop(cell_props, "source")
+ row.prop(cell_props, "source_vert_own")
+ row.prop(cell_props, "source_particle_own")
+ row.prop(cell_props, "source_random")
+ row = col.row()
+ row.prop(cell_props, "source_vert_child")
+ row.prop(cell_props, "source_particle_child")
+ row.prop(cell_props, "source_pencil")
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Transform")
+ row = col.row()
+ row.prop(cell_props, "pre_simplify")
+ row.prop(cell_props, "source_noise")
+ row.prop(cell_props, "margin")
+ row = col.row(align=True)
+
+ row.prop(cell_props, "cell_scale")
+ row.prop(cell_props, "use_recenter")
+ # could be own section, control how we subdiv
+ #row.prop(cell_props, "use_island_split")
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Recursive Shatter")
+ row = col.row(align=True)
+ row.alignment = 'LEFT'
+ row.prop(cell_props, "recursion")
+ row = col.row(align=True)
+ row.alignment = 'LEFT'
+ if cell_props.recursion > 0:
+ row.enabled = True
+ else:
+ row.enabled = False
+ row.prop(cell_props, "recursion_source_limit")
+ row.prop(cell_props, "recursion_clamp")
+ row.prop(cell_props, "recursion_chance")
+ row.prop(cell_props, "recursion_chance_select")#, expand=True)
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Interior Meshes")
+ row = col.row(align=True)
+ row.alignment = 'LEFT'
+ row.prop(cell_props, "use_data_match")
+ row.prop(cell_props, "use_interior_vgroup")
+ row.prop(cell_props, "use_smooth_faces")
+ row.prop(cell_props, "use_sharp_edges")
+ if cell_props.use_sharp_edges == True:
+ row.prop(cell_props, "use_sharp_edges_apply")
+
+ row = col.row()
+ if cell_props.use_data_match == True:
+ row.enabled = True
+ else:
+ row.enabled = False
+ row.alignment = 'LEFT'
+ # on same row for even layout but infact are not all that related
+ row.prop(cell_props, "material_index")
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Custom Properties")
+ row = col.row(align=True)
+ row.prop(cell_props, "use_mass")
+ if cell_props.use_mass:
+ row = col.row(align=True)
+ row.prop(cell_props, "mass_name")
+ row = col.row(align=True)
+ row.prop(cell_props, "mass_mode")
+ row.prop(cell_props, "mass")
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Scene Management")
+ row = col.row(align=True)
+ row.alignment = 'LEFT'
+ row.prop(cell_props, "original_hide")
+ row.prop(cell_props, "cell_relocate")
+ row = col.row(align=True)
+ row.prop(cell_props, "use_collection")
+ if cell_props.use_collection:
+ row.prop(cell_props, "new_collection")
+ row.prop(cell_props, "collection_name")
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Debug")
+ row = col.row(align=True)
+ row.prop(cell_props, "use_debug_redraw")
+ row.prop(cell_props, "use_debug_points")
+ row.prop(cell_props, "use_debug_bool")
+
+
+class FRACTURE_OT_Crack(Operator):
+ bl_idname = "object.add_fracture_crack"
+ bl_label = "Cell To Crack"
+ bl_description = "Make a cracked object from cell objects"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self, width=600)
+
+ def execute(self, context):
+ crack_props = context.window_manager.fracture_crack_props
+
+ cells = context.selected_editable_objects
+ object = None
+
+ if cells:
+ # clear sharp edges for correct crack surface.
+ bpy.context.view_layer.objects.active = cells[0]
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.reveal()
+
+ bpy.ops.mesh.mark_sharp(clear=True, use_verts=True)
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ for cell in cells:
+ bpy.context.view_layer.objects.active = cell
+ bpy.ops.object.modifier_remove(modifier="EDGE_SPLIT_cell")
+ bpy.context.object.vertex_groups.clear()
+
+ bpy.context.view_layer.objects.active = cells[0]
+ object = crack_functions.make_join(cells)
+
+ if object:
+ bpy.context.view_layer.objects.active = object
+
+ crack_functions.add_modifiers()
+ bpy.context.object.modifiers['DECIMATE_crackit'].ratio = crack_props.modifier_decimate
+ bpy.context.object.modifiers['SMOOTH_crackit'].factor = crack_props.modifier_smooth
+
+ crack_functions.multiExtrude(
+ off=0.1,
+ rotx=0, roty=0, rotz=0,
+ sca=crack_props.extrude_scale,
+ var1=crack_props.extrude_var, var2=crack_props.extrude_var, var3=crack_props.extrude_var,
+ num=crack_props.extrude_num, ran=0
+ )
+ bpy.ops.object.modifier_apply(apply_as='DATA', modifier='DECIMATE_crackit')
+ bpy.ops.object.shade_smooth()
+
+ if crack_props.modifier_wireframe == True:
+ bpy.ops.object.modifier_add(type='WIREFRAME')
+ wireframe = bpy.context.object.modifiers[-1]
+ wireframe.name = 'WIREFRAME_crackit'
+ wireframe.use_even_offset = False
+ wireframe.thickness = 0.01
+ else:
+ assert("Joining into One object had been failed. Mesh object can only be joined.")
+
+ return {'FINISHED'}
+
+ def draw(self, context):
+ cell_props = context.window_manager.fracture_cell_props
+ crack_props = context.window_manager.fracture_crack_props
+ layout = self.layout
+
+ box = layout.box()
+ col = box.column()
+ col.label(text='* Execute After "1. Cell Fracture"')
+
+ box = layout.box()
+ col = box.column()
+ col.label(text="Surface:")
+ row = col.row(align=True)
+ row.alignment = 'LEFT'
+ row.prop(crack_props, "modifier_decimate")
+ row.prop(crack_props, "modifier_smooth")
+
+ col = box.column()
+ col.label(text="Extrude:")
+ row = col.row(align=True)
+ row.prop(crack_props, "extrude_scale")
+ row.prop(crack_props, "extrude_var")
+ row.prop(crack_props, "extrude_num")
+
+ col = box.column()
+ col.label(text="Post Processing")
+ row = col.row(align=True)
+ row.prop(crack_props, "modifier_wireframe")
+
+
+class FRACTURE_OT_Material(Operator):
+ bl_idname = "object.add_fracture_material"
+ bl_label = "Material Preset"
+ bl_description = ("Material preset for cracked object")
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ # included - type that can have materials
+ included = ['MESH', 'CURVE', 'SURFACE', 'FONT', 'META']
+ return (obj is not None and obj.type in included)
+
+ def execute(self, context):
+ material_props = context.window_manager.fracture_material_props
+
+ mat_name = material_props.material_preset
+ mat_lib_name = material_props.material_lib_name
+ mat_ui_name = material_props.get_ui_mat_name(mat_name) if not mat_lib_name else mat_name
+
+ try:
+ material_functions.appendMaterial(
+ mat_lib_name = mat_lib_name,
+ mat_name = mat_name,
+ mat_ui_names = mat_ui_name
+ )
+ except Exception as e:
+ material_functions.error_handlers(
+ self, "mesh.crackit_material", e,
+ "The active Object could not have the Material {} applied".format(mat_ui_name)
+ )
+ return {"CANCELLED"}
+
+ return {'FINISHED'} \ No newline at end of file
diff --git a/object_fracture_cell/process/cell_calc.py b/object_fracture_cell/process/cell_calc.py
new file mode 100644
index 00000000..272ebff1
--- /dev/null
+++ b/object_fracture_cell/process/cell_calc.py
@@ -0,0 +1,149 @@
+# ##### 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_to_verts(original_xyz_minmax,
+ points,
+ points_scale=None,
+ margin_bounds=0.05,
+ margin_cell=0.0):
+
+ from math import sqrt
+ import mathutils
+ from mathutils import Vector
+
+ cells = []
+ 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:
+ xmin, xmax = original_xyz_minmax["x"]
+ ymin, ymax = original_xyz_minmax["y"]
+ zmin, zmax = original_xyz_minmax["z"]
+
+ xmin -= margin_bounds
+ xmax += margin_bounds
+ ymin -= margin_bounds
+ ymax += margin_bounds
+ zmin -= margin_bounds
+ zmax += margin_bounds
+
+ # (x,y,z,scaler) for plane. xyz is normaliized direction. scaler is scale for plane.
+ # Plane will be made at the perpendicular direction of the normal vector.
+ 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)),
+ ]
+
+ if len(points) > 1:
+ points_dist_sorted = [(Vector(p[0]), p[1]) for p in points]
+
+ for i, point_current in enumerate(points):
+ planes = [None] * len(convexPlanes)
+ for j in range(len(convexPlanes)):
+ planes[j] = convexPlanes[j].copy()
+ # e.g. Dot product point's (xyz) with convex's (+1.0,0.0,0.0) detects x value of the point.
+ # e.g. Then, x scaler += point's x value.
+ planes[j][3] += planes[j].xyz.dot(point_current[0])
+
+ distance_max = 10000000000.0 # a big value!
+
+ points_dist_sorted_current = points_dist_sorted.copy()
+ # Closer points to the current point are earlier order. Of course, current point is the first.
+ points_dist_sorted_current.sort(key=lambda p: (p[0] - point_current[0]).length_squared)
+
+ # The point itself is removed.
+ points_dist_sorted_current.pop(0)
+
+ # Compare the current point with other points.
+ for j in range(len(points_dist_sorted_current)):
+
+ point_target = points_dist_sorted_current[j]
+ normal = 0
+ normal = point_target[0] - point_current[0]
+ nlength = normal.length # is sqrt(X^2+y^2+z^2).
+
+ 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
+ # Scale rate (normal_alt/normal). If these are the same, dot product is 1.
+ scalar = normal_alt.normalized().dot(normal.normalized())
+ # assert(scalar >= 0.0)
+ nlength *= scalar
+ normal = normal_alt
+
+ if nlength > distance_max:
+ break
+
+ # 4D vector, the same form as convexPlanes. (x,y,z,scaler).
+ plane = normal.normalized()
+ plane.resize_4d()
+ plane[3] = (-nlength / 2.0) + margin_cell
+ planes.append(plane)
+
+ # Make vertex points of cell, by crossing point of planes.
+ 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 ここでルートでマックスを下げているのか?でも下で2倍にしているが。
+ distance_max *= 2.0
+
+ if len(vertices) == 0:
+ continue
+
+ cells.append((point_current[0], vertices[:]))
+ del vertices[:]
+
+ else:
+ vertices[:], plane_indices[:] = mathutils.geometry.points_in_planes(convexPlanes)
+ #convex_center = Vector(((xmin-xmax)/2, (ymin-ymax)/2, (zmin-zmax)/2))
+ convex_center = Vector((0,0,0))
+ cells.append((convex_center, vertices[:]))
+
+ return cells \ No newline at end of file
diff --git a/object_fracture_cell/process/cell_functions.py b/object_fracture_cell/process/cell_functions.py
new file mode 100644
index 00000000..80f77870
--- /dev/null
+++ b/object_fracture_cell/process/cell_functions.py
@@ -0,0 +1,599 @@
+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 _limit_source(points, source_limit):
+ if source_limit != 0 and source_limit < len(points):
+ import random
+ random.shuffle(points)
+ points[source_limit:] = []
+ return points
+ else:
+ return points
+
+
+def simplify_original(original, pre_simplify):
+ bpy.context.view_layer.objects.active = original
+ bpy.ops.object.modifier_add(type='DECIMATE')
+ decimate = bpy.context.object.modifiers[-1]
+ decimate.name = 'DECIMATE_crackit_original'
+ decimate.ratio = 1-pre_simplify
+
+def desimplify_original(original):
+ bpy.context.view_layer.objects.active = original
+ if 'DECIMATE_crackit_original' in bpy.context.object.modifiers.keys():
+ bpy.ops.object.modifier_remove(modifier='DECIMATE_crackit_original')
+
+def original_minmax(original_verts):
+ xa = [v[0] for v in original_verts]
+ ya = [v[1] for v in original_verts]
+ za = [v[2] for v in original_verts]
+ xmin, xmax = min(xa), max(xa)
+ ymin, ymax = min(ya), max(ya)
+ zmin, zmax = min(za), max(za)
+ return {"x":(xmin,xmax), "y":(ymin,ymax), "z":(zmin,zmax)}
+
+def points_from_object(original, original_xyz_minmax,
+ source_vert_own=100,
+ source_vert_child=0,
+ source_particle_own=0,
+ source_particle_child=0,
+ source_pencil=0,
+ source_random=0):
+
+ points = []
+
+ # This is not used by anywhere
+ def edge_center(mesh, edge):
+ v1, v2 = edge.vertices
+ return (mesh.vertices[v1].co + mesh.vertices[v2].co) / 2.0
+
+ # This is not used by anywhere
+ 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(original):
+ """Takes points from _any_ object with geometry"""
+ if original.type == 'MESH':
+ mesh = original.data
+ matrix = original.matrix_world.copy()
+ p = [(matrix @ v.co, 'VERTS') for v in mesh.vertices]
+ return p
+ else:
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ ob_eval = original.evaluated_get(depsgraph)
+ try:
+ mesh = ob_eval.to_mesh()
+ except:
+ mesh = None
+
+ if mesh is not None:
+ matrix = original.matrix_world.copy()
+ p = [(matrix @ v.co, 'VERTS') for v in mesh.vertices]
+ ob_eval.to_mesh_clear()
+ return p
+
+ def points_from_particles(original):
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ obj_eval = original.evaluated_get(depsgraph)
+
+ p = [(particle.location.copy(), 'PARTICLE')
+ for psys in obj_eval.particle_systems
+ for particle in psys.particles]
+ return p
+
+ def points_from_random(original, original_xyz_minmax):
+ xmin, xmax = original_xyz_minmax["x"]
+ ymin, ymax = original_xyz_minmax["y"]
+ zmin, zmax = original_xyz_minmax["z"]
+
+ from random import uniform
+ from mathutils import Vector
+
+ p = []
+ for i in range(source_random):
+ new_pos = Vector( (uniform(xmin, xmax), uniform(ymin, ymax), uniform(zmin, zmax)) )
+ p.append((new_pos, 'RANDOM'))
+ return p
+
+ # geom own
+ if source_vert_own > 0:
+ new_points = points_from_verts(original)
+ new_points = _limit_source(new_points, source_vert_own)
+ points.extend(new_points)
+
+ # random
+ if source_random > 0:
+ new_points = points_from_random(original, original_xyz_minmax)
+ points.extend(new_points)
+
+
+ # geom children
+ if source_vert_child > 0:
+ for original_child in original.children:
+ new_points = points_from_verts(original_child)
+ new_points = _limit_source(new_points, source_vert_child)
+ points.extend(new_points)
+
+ # geom particles
+ if source_particle_own > 0:
+ new_points = points_from_particles(original)
+ new_points = _limit_source(new_points, source_particle_own)
+ points.extend(new_points)
+
+ if source_particle_child > 0:
+ for original_child in original.children:
+ new_points = points_from_particles(original_child)
+ new_points = _limit_source(new_points, source_particle_child)
+ points.extend(new_points)
+
+ # grease pencil
+ def get_points(stroke):
+ return [point.co.copy() for point in stroke.points]
+
+ def get_splines(gp):
+ gpl = gp.layers.active
+ if gpl:
+ fr = gpl.active_frame
+ if not fr:
+ current = bpy.context.scene.frame_current
+ gpl.frames.new(current)
+ gpl.active_frame = current
+ fr = gpl.active_frame
+
+ return [get_points(stroke) for stroke in fr.strokes]
+ else:
+ return []
+
+ if source_pencil > 0:
+ gp = bpy.context.scene.grease_pencil
+ if gp:
+ line_points = []
+ line_points = [(p, 'PENCIL') for spline in get_splines(gp)
+ for p in spline]
+ if len(line_points) > 0:
+ line_points = _limit_source(line_points, source_pencil)
+
+ # Make New point between the line point and the closest point.
+ if not points:
+ points.extend(line_points)
+
+ else:
+ for lp in line_points:
+ # Make vector between the line point and its closest point.
+ points.sort(key=lambda p: (p[0] - lp[0]).length_squared)
+ closest_point = points[0]
+ normal = lp[0].xyz - closest_point[0].xyz
+
+ new_point = (lp[0], lp[1])
+ new_point[0].xyz += normal / 2
+
+ points.append(new_point)
+ #print("Found %d points" % len(points))
+ return points
+
+
+def points_to_cells(context, original, original_xyz_minmax, points,
+ source_limit=0,
+ source_noise=0.0,
+ 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),
+ clean=True):
+
+ from . import cell_calc
+ collection = context.collection
+ view_layer = context.view_layer
+
+ # apply optional clamp
+ if source_limit != 0 and source_limit < len(points):
+ points = _limit_source(points, source_limit)
+
+ # saddly we cant be sure there are no doubles
+ from mathutils import Vector
+ to_tuple = Vector.to_tuple
+
+ # To remove doubles, round the values.
+ points = [(Vector(to_tuple(p[0], 4)),p[1]) for p in points]
+ del to_tuple
+ del Vector
+
+ if source_noise > 0.0:
+ from random import random
+ # boundbox approx of overall scale
+ from mathutils import Vector
+ matrix = original.matrix_world.copy()
+ bb_world = [matrix @ Vector(v) for v in original.bound_box]
+ scalar = source_noise * ((bb_world[0] - bb_world[6]).length / 2.0)
+
+ from mathutils.noise import random_unit_vector
+ points[:] = [(p[0] + (random_unit_vector() * (scalar * random())), p[1]) for p in points]
+
+ if use_debug_points:
+ bm = bmesh.new()
+ for p in points:
+ bm.verts.new(p[0])
+ 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
+
+ cells_verts = cell_calc.points_to_verts(original_xyz_minmax,
+ points,
+ cell_scale,
+ margin_cell=margin)
+ # some hacks here :S
+ cell_name = original.name + "_cell"
+ cells = []
+ for center_point, cell_verts in cells_verts:
+ # ---------------------------------------------------------------------
+ # 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
+
+ for i, co in enumerate(cell_verts):
+ co.x += R()
+ co.y += R()
+ co.z += R()
+ bm_vert = bm.verts.new(co)
+
+ import mathutils
+ bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005)
+ try:
+ # Making cell meshes as convex full here!
+ 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()
+ # smooth faces will remain only inner faces, after appling boolean modifier.
+ 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 = original.data
+ for mat in mesh_src.materials:
+ mesh_dst.materials.append(mat)
+ for lay_attr in ("vertex_colors", "uv_layers"):
+ 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
+ cell = bpy.data.objects.new(name=cell_name, object_data=mesh_dst)
+ collection.objects.link(cell)
+ cell.location = center_point
+ cells.append(cell)
+
+ # support for object materials
+ if use_data_match:
+ for i in range(len(mesh_dst.materials)):
+ slot_src = original.material_slots[i]
+ slot_dst = 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...
+ # Blender 2.8: BGE integration was disabled, --
+ # -- because BGE was deleted in Blender 2.8.
+ '''
+ for cell in cells:
+ game = cell.game
+ game.physics_type = 'RIGID_BODY'
+ game.use_collision_bounds = True
+ game.collision_bounds_type = 'CONVEX_HULL'
+ '''
+ return cells
+
+
+def cell_boolean(context, original, cells,
+ use_debug_bool=False,
+ clean=True,
+ use_island_split=False,
+ use_interior_hide=False,
+ use_debug_redraw=False,
+ level=0,
+ remove_doubles=True
+ ):
+
+ cells_boolean = []
+ collection = context.collection
+ scene = context.scene
+ view_layer = context.view_layer
+
+ if use_interior_hide and level == 0:
+ # only set for level 0
+ original.data.polygons.foreach_set("hide", [False] * len(original.data.polygons))
+
+ # The first object can't be applied by bool, so it is used as a no-effect first straw-man.
+ bpy.ops.mesh.primitive_cube_add(enter_editmode=False, location=(original.location.x+10000000000.0, 0, 0))
+ temp_cell = bpy.context.active_object
+ cells.insert(0, temp_cell)
+
+ bpy.ops.object.select_all(action='DESELECT')
+ for i, cell in enumerate(cells):
+ if not use_debug_bool:
+ if use_interior_hide:
+ cell.data.polygons.foreach_set("hide", [True] * len(cell.data.polygons))
+
+ # mesh_old should be made before appling boolean modifier.
+ mesh_old = cell.data
+
+ original.select_set(True)
+ cell.select_set(True)
+ bpy.context.view_layer.objects.active = cell
+ bpy.ops.btool.boolean_inters()
+ bpy.ops.object.modifier_apply(apply_as='DATA', modifier="BTool_" + original.name)
+
+ if i == 0:
+ bpy.data.objects.remove(cell, do_unlink=True)
+ continue
+
+ cell = bpy.context.active_object
+ cell.select_set(False)
+
+ # depsgraph sould be gotten after applied boolean modifier, for new_mesh.
+ depsgraph = context.evaluated_depsgraph_get()
+ cell_eval = cell.evaluated_get(depsgraph)
+
+ mesh_new = bpy.data.meshes.new_from_object(cell_eval)
+ cell.data = mesh_new
+
+ '''
+ check_hide = [11] * len(cell.data.polygons)
+ cell.data.polygons.foreach_get("hide", check_hide)
+ print(check_hide)
+ '''
+
+ # remove if not valid
+ if not mesh_old.users:
+ bpy.data.meshes.remove(mesh_old)
+ if not mesh_new.vertices:
+ collection.objects.unlink(cell)
+ if not cell.users:
+ bpy.data.objects.remove(cell)
+ 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 cell is not None:
+ cells_boolean.append(cell)
+
+ if use_debug_redraw:
+ _redraw_yasiamevil()
+
+ bpy.context.view_layer.objects.active = original
+ bpy.ops.btool.remove(thisObj=original.name, Prop="THIS")
+
+ 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(False)
+ for cell in cells_boolean:
+ cell.select_set(True)
+ # If new separated meshes are made, selected objects is increased.
+ if cells_boolean:
+ bpy.ops.mesh.separate(type='LOOSE')
+
+ cells_boolean[:] = [cell for cell in scene.objects if cell.select_get()]
+
+ context.view_layer.update()
+ return cells_boolean
+
+
+def interior_handle(cells,
+ 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 cell in cells:
+ mesh = 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
+ cell.vertex_groups.new(name="Interior")
+
+ if use_sharp_edges:
+ bpy.context.space_data.overlay.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:
+ bpy.context.view_layer.objects.active = cell
+ bpy.ops.object.modifier_add(type='EDGE_SPLIT')
+
+ edge_split = bpy.context.object.modifiers[-1]
+ edge_split.name = 'EDGE_SPLIT_cell'
+ edge_split.use_edge_angle = False
+
+ '''
+ 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()
+
+
+def post_process(cells,
+ use_collection=False,
+ new_collection=False,
+ collection_name="Fracture",
+ use_mass=False,
+ mass=1.0,
+ mass_mode='VOLUME', mass_name='mass',
+ ):
+
+ """Run after Interiro handle"""
+ #--------------
+ # Collection Options
+ if use_collection:
+ colle = None
+ if not new_collection:
+ colle = bpy.data.collections.get(collection_name)
+
+ if colle is None:
+ colle = bpy.data.collections.new(collection_name)
+
+ # THe collection should be children of master collection to show in outliner.
+ child_names = [m.name for m in bpy.context.scene.collection.children]
+ if colle.name not in child_names:
+ bpy.context.scene.collection.children.link(colle)
+
+ # Cell objects are only link to the collection.
+ bpy.ops.collection.objects_remove_all() # For all selected object.
+ for colle_obj in cells:
+ colle.objects.link(colle_obj)
+
+ #--------------
+ # Mass Options
+ if use_mass:
+ # Blender 2.8: Mass for BGE was no more available.--
+ # -- Instead, Mass values is used for custom properies on cell objects.
+ if mass_mode == 'UNIFORM':
+ for cell in cells:
+ #cell.game.mass = mass
+ cell[mass_name] = mass
+ elif mass_mode == 'VOLUME':
+ from mathutils import Vector
+ def _get_volume(cell):
+ def _getObjectBBMinMax():
+ min_co = Vector((1000000.0, 1000000.0, 1000000.0))
+ max_co = -min_co
+ matrix = cell.matrix_world
+ for i in range(0, 8):
+ bb_vec = cell.matrix_world @ Vector(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()
+
+ cell_volume_ls = [_get_volume(cell) for cell in cells]
+ cell_volume_tot = sum(cell_volume_ls)
+ if cell_volume_tot > 0.0:
+ mass_fac = mass / cell_volume_tot
+ for i, cell in enumerate(cells):
+ cell[mass_name] = cell_volume_ls[i] * mass_fac
+ else:
+ assert(0) \ No newline at end of file
diff --git a/object_fracture_cell/process/cell_main.py b/object_fracture_cell/process/cell_main.py
new file mode 100644
index 00000000..8283b5a2
--- /dev/null
+++ b/object_fracture_cell/process/cell_main.py
@@ -0,0 +1,208 @@
+if "bpy" in locals():
+ import importlib
+ importlib.reload(cell_functions)
+
+else:
+ from . import cell_functions
+
+import bpy
+
+
+def main_object(context, original, level, **kw):
+ import random
+
+ # pull out some args
+ kw_copy = kw.copy()
+ source_vert_own = kw_copy.pop("source_vert_own")
+ source_vert_child = kw_copy.pop("source_vert_child")
+ source_particle_own = kw_copy.pop("source_particle_own")
+ source_particle_child = kw_copy.pop("source_particle_child")
+ source_pencil = kw_copy.pop("source_pencil")
+ source_random = kw_copy.pop("source_random")
+
+ use_recenter = kw_copy.pop("use_recenter")
+ 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_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")
+
+ cell_relocate = kw_copy.pop("cell_relocate")
+
+ collection = context.collection
+ scene = context.scene
+
+ if level != 0:
+ kw_copy["source_limit"] = recursion_source_limit
+
+ from . import cell_functions
+
+ # not essential but selection is visual distraction.
+ original.select_set(False)
+
+ if kw_copy["use_debug_redraw"]:
+ original_display_type_prev = original.display_type
+ original.display_type = 'WIRE'
+
+ original_mesh = original.data
+ original_matrix = original.matrix_world.copy()
+ original_verts = [original_matrix @ v.co for v in original_mesh.vertices]
+ original_xyz_minmax = cell_functions.original_minmax(original_verts)
+
+ cells = []
+ points = cell_functions.points_from_object(original, original_xyz_minmax,
+ source_vert_own=source_vert_own,
+ source_vert_child=source_vert_child,
+ source_particle_own=source_particle_own,
+ source_particle_child=source_particle_child,
+ source_pencil=source_pencil,
+ source_random=source_random)
+
+ cells = cell_functions.points_to_cells(context, original, original_xyz_minmax, points, **kw_copy)
+ cells = cell_functions.cell_boolean(context, original, cells,
+ 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": cells},
+ type='ORIGIN_GEOMETRY', center='MEDIAN')
+
+ #--------------
+ # Recursion.
+ if level == 0:
+ for level_sub in range(1, recursion + 1):
+
+ objects_recurse_input = [(i, o) for i, o in enumerate(cells)]
+ 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(cells[i] is obj_cell)
+ # Repeat main_object() here.
+ objects_recursive += main_object(context, obj_cell, level_sub, **kw)
+ #if original_remove:
+ collection.objects.unlink(obj_cell)
+ del cells[i]
+ if recursion_clamp and len(cells) + len(objects_recursive) >= recursion_clamp:
+ break
+ cells.extend(objects_recursive)
+
+ if recursion_clamp and len(cells) > recursion_clamp:
+ break
+
+ #--------------
+ # Level Options
+ if level == 0:
+ # import pdb; pdb.set_trace()
+ if use_interior_vgroup or use_sharp_edges:
+ cell_functions.interior_handle(cells,
+ use_interior_vgroup=use_interior_vgroup,
+ use_sharp_edges=use_sharp_edges,
+ use_sharp_edges_apply=use_sharp_edges_apply,
+ )
+
+ if cell_relocate:
+ for cell in cells:
+ cell.location.x += (original_xyz_minmax["x"][1] - original_xyz_minmax["x"][0]) + 1
+
+ if kw_copy["use_debug_redraw"]:
+ original.display_type = original_display_type_prev
+
+ return cells
+
+
+def main(context, original, **kw):
+ '''
+ import time
+ t = time.time()
+ '''
+
+ kw_copy = kw.copy()
+
+ # Pre_Simplify
+ pre_simplify = kw_copy.pop("pre_simplify")
+ # collection
+ use_collection = kw_copy.pop("use_collection")
+ new_collection = kw_copy.pop("new_collection")
+ collection_name = kw_copy.pop("collection_name")
+ # object visibility
+ original_hide = kw_copy.pop("original_hide")
+ # mass
+ use_mass = kw_copy.pop("use_mass")
+ mass_name = kw_copy.pop("mass_name")
+ mass_mode = kw_copy.pop("mass_mode")
+ mass = kw_copy.pop("mass")
+
+ cells = []
+
+ if original.type == 'MESH':
+ if pre_simplify > 0.0:
+ cell_functions.simplify_original(original=original, pre_simplify=pre_simplify)
+
+ cells += main_object(context, original, 0, **kw_copy)
+
+ if pre_simplify > 0.0:
+ cell_functions.desimplify_original(original=original)
+ else:
+ assert obj.type == 'MESH', "No MESH object selected."
+
+ bpy.ops.object.select_all(action='DESELECT')
+
+ for cell in cells:
+ cell.select_set(True)
+
+ cell_functions.post_process(cells,
+ use_collection=use_collection,
+ new_collection=new_collection,
+ collection_name=collection_name,
+ use_mass=use_mass,
+ mass=mass,
+ mass_mode=mass_mode,
+ mass_name=mass_name,
+ )
+
+ # To avoid select both original object and cells in EDIT mode.
+ bpy.context.view_layer.objects.active = cells[0]
+
+ # de-hide all objects and meshes.
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.reveal()
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ if original_hide:
+ original.hide_set(True)
+
+ #print("Done! %d objects in %.4f sec" % (len(cells), time.time() - t))
+ #print("Done!")
+ return (original, cells) \ No newline at end of file
diff --git a/object_fracture_cell/process/crack_functions.py b/object_fracture_cell/process/crack_functions.py
new file mode 100644
index 00000000..ff7e93a3
--- /dev/null
+++ b/object_fracture_cell/process/crack_functions.py
@@ -0,0 +1,140 @@
+# gpl: author Nobuyuki Hirakata
+
+import bpy
+
+import bmesh
+from random import (
+ gauss,
+ seed,
+ )
+from math import radians, pi
+from mathutils import Euler
+
+
+# Join fractures into an object
+def make_join(cells):
+
+ # Execute join
+ bpy.context.view_layer.objects.active = cells[0]
+ cells[0].select_set(state=True)
+ bpy.ops.object.join()
+
+ bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
+
+ joined = bpy.context.active_object
+
+ suffix_index = joined.name.rfind("_cell")
+ if suffix_index != -1:
+ joined.name = joined.name[:suffix_index] + "_crack"
+
+ return bpy.context.active_object
+
+
+# Add modifier and setting
+def add_modifiers(decimate_val=0.4, smooth_val=0.5):
+ bpy.ops.object.modifier_add(type='DECIMATE')
+ decimate = bpy.context.object.modifiers[-1]
+ decimate.name = 'DECIMATE_crackit'
+ decimate.ratio = decimate_val
+
+ bpy.ops.object.modifier_add(type='SUBSURF')
+ subsurf = bpy.context.object.modifiers[-1]
+ subsurf.name = 'SUBSURF_crackit'
+
+ bpy.ops.object.modifier_add(type='SMOOTH')
+ smooth = bpy.context.object.modifiers[-1]
+ smooth.name = 'SMOOTH_crackit'
+ smooth.factor = smooth_val
+
+
+# -------------- multi extrude --------------------
+# var1=random offset, var2=random rotation, var3=random scale
+def multiExtrude(off=0.1, rotx=0, roty=0, rotz=0, sca=0.0,
+ var1=0.01, var2=0.01, var3=0.01, num=1, ran=0):
+
+ obj = bpy.context.object
+ bpy.context.tool_settings.mesh_select_mode = [False, False, True]
+
+ # bmesh operations
+ bpy.ops.object.mode_set()
+ bm = bmesh.new()
+ bm.from_mesh(obj.data)
+ sel = [f for f in bm.faces if f.select]
+
+ # faces loop
+ for i, of in enumerate(sel):
+ rot = _vrot(r=i, ran=ran, rotx=rotx, var2=var2, roty=roty, rotz=rotz)
+ off = _vloc(r=i, ran=ran, off=off, var1=var1)
+ of.normal_update()
+
+ # extrusion loop
+ for r in range(num):
+ nf = of.copy()
+ nf.normal_update()
+ no = nf.normal.copy()
+ ce = nf.calc_center_bounds()
+ s = _vsca(r=i + r, ran=ran, var3=var3, sca=sca)
+
+ for v in nf.verts:
+ v.co -= ce
+ v.co.rotate(rot)
+ v.co += ce + no * off
+ v.co = v.co.lerp(ce, 1 - s)
+
+ # extrude code from TrumanBlending
+ for a, b in zip(of.loops, nf.loops):
+ sf = bm.faces.new((a.vert, a.link_loop_next.vert,
+ b.link_loop_next.vert, b.vert))
+ sf.normal_update()
+
+ bm.faces.remove(of)
+ of = nf
+
+ for v in bm.verts:
+ v.select = False
+
+ for e in bm.edges:
+ e.select = False
+
+ bm.to_mesh(obj.data)
+ obj.data.update()
+
+
+def _vloc(r, ran, off, var1):
+ seed(ran + r)
+ return off * (1 + gauss(0, var1 / 3))
+
+def _vrot(r, ran, rotx, var2, roty, rotz):
+ seed(ran + r)
+ return Euler((radians(rotx) + gauss(0, var2 / 3),
+ radians(roty) + gauss(0, var2 / 3),
+ radians(rotz) + gauss(0, var2 / 3)), 'XYZ')
+
+def _vsca(r, ran, sca, var3):
+ seed(ran + r)
+ return sca * (1 + gauss(0, var3 / 3))
+
+# Centroid of a selection of vertices
+'''
+def _centro(ver):
+ vvv = [v for v in ver if v.select]
+ if not vvv or len(vvv) == len(ver):
+ return ('error')
+
+ x = sum([round(v.co[0], 4) for v in vvv]) / len(vvv)
+ y = sum([round(v.co[1], 4) for v in vvv]) / len(vvv)
+ z = sum([round(v.co[2], 4) for v in vvv]) / len(vvv)
+
+ return (x, y, z)
+'''
+
+# Retrieve the original state of the object
+'''
+def _volver(obj, copia, om, msm, msv):
+ for i in copia:
+ obj.data.vertices[i].select = True
+ bpy.context.tool_settings.mesh_select_mode = msm
+
+ for i in range(len(msv)):
+ obj.modifiers[i].show_viewport = msv[i]
+'''
diff --git a/object_fracture_cell/process/material_functions.py b/object_fracture_cell/process/material_functions.py
new file mode 100644
index 00000000..938474d4
--- /dev/null
+++ b/object_fracture_cell/process/material_functions.py
@@ -0,0 +1,83 @@
+# gpl: author Nobuyuki Hirakata
+
+import bpy
+import os
+
+
+# Allow changing the original material names from the .blend file
+# by replacing them with the UI Names from the EnumProperty
+def get_ui_mat_name(mat_name):
+ mat_ui_name = "CrackIt Material"
+ try:
+ # access the Scene type directly to get the name from the enum
+ mat_items = bpy.types.Scene.crackit[1]["type"].bl_rna.material_preset[1]["items"]
+ for mat_id, mat_list in enumerate(mat_items):
+ if mat_name in mat_list:
+ mat_ui_name = mat_items[mat_id][1]
+ break
+ del mat_items
+ except Exception as e:
+ error_handlers(
+ False, "get_ui_mat_name", e,
+ "Retrieving the EnumProperty key UI Name could not be completed", True
+ )
+ pass
+
+ return mat_ui_name
+
+
+# error_type='ERROR' for popup massage
+def error_handlers(self, op_name, error, reports="ERROR", func=False, error_type='WARNING'):
+ if self and reports:
+ self.report({error_type}, reports + " (See Console for more info)")
+
+ is_func = "Function" if func else "Operator"
+ print("\n[Cell Fracture Crack It]\n{}: {}\nError: "
+ "{}\nReport: {}\n".format(is_func, op_name, error, reports))
+
+
+def appendMaterial(mat_lib_name, mat_name, mat_ui_names="Nameless Material"):
+ file_path = _makeFilePath(os.path.dirname(__file__))
+ bpy.ops.wm.append(filename=mat_name, directory=file_path)
+
+ # If material is loaded some times, select the last-loaded material
+ last_material = _getAppendedMaterial(mat_name)
+
+ if last_material:
+ mat = bpy.data.materials[last_material]
+ # skip renaming if the prop is True
+ if not mat_lib_name:
+ mat.name = mat_ui_names
+
+ # Apply Only one material in the material slot
+ for m in bpy.context.object.data.materials:
+ bpy.ops.object.material_slot_remove()
+
+ bpy.context.object.data.materials.append(mat)
+
+ return True
+
+ return False
+
+
+# Make file path of addon
+def _makeFilePath(addon_path):
+ material_folder = "/materials"
+ blend_file = "/materials1.blend"
+ category = "\\Material\\"
+
+ file_path = addon_path + material_folder + blend_file + category
+ return file_path
+
+
+# Get last-loaded material, such as ~.002
+def _getAppendedMaterial(material_name):
+ # Get material name list
+ material_names = [m.name for m in bpy.data.materials if material_name in m.name]
+ if material_names:
+ # Return last material in the sorted order
+ material_names.sort()
+
+ return material_names[-1]
+
+ return None
diff --git a/object_fracture_cell/process/materials/materials1.blend b/object_fracture_cell/process/materials/materials1.blend
new file mode 100644
index 00000000..df865ee8
--- /dev/null
+++ b/object_fracture_cell/process/materials/materials1.blend
Binary files differ
diff --git a/object_fracture_cell/utilities.py b/object_fracture_cell/utilities.py
new file mode 100644
index 00000000..705d3452
--- /dev/null
+++ b/object_fracture_cell/utilities.py
@@ -0,0 +1,42 @@
+
+def _cell_props_to_dict(fracture_cell_props):
+ cell_keywords = {
+ 'source_vert_own': fracture_cell_props.source_vert_own,
+ 'source_vert_child': fracture_cell_props.source_vert_child,
+ 'source_particle_own': fracture_cell_props.source_particle_own,
+ 'source_particle_child': fracture_cell_props.source_particle_child,
+ 'source_pencil': fracture_cell_props.source_pencil,
+ 'source_random': fracture_cell_props.source_random,
+ 'source_noise': fracture_cell_props.source_noise,
+ 'margin': fracture_cell_props.margin,
+ 'cell_scale': fracture_cell_props.cell_scale,
+ 'pre_simplify': fracture_cell_props.pre_simplify,
+ 'use_recenter': fracture_cell_props.use_recenter,
+ 'use_island_split': fracture_cell_props.use_island_split,
+ 'recursion': fracture_cell_props.recursion,
+ 'recursion_source_limit': fracture_cell_props.recursion_source_limit,
+ 'recursion_clamp': fracture_cell_props.recursion_clamp,
+ 'recursion_chance': fracture_cell_props.recursion_chance,
+ 'recursion_chance_select': fracture_cell_props.recursion_chance_select,
+ 'use_smooth_faces': fracture_cell_props.use_smooth_faces,
+ 'use_sharp_edges': fracture_cell_props.use_sharp_edges,
+ 'use_sharp_edges_apply': fracture_cell_props.use_sharp_edges_apply,
+ 'use_data_match': fracture_cell_props.use_data_match,
+ 'material_index': fracture_cell_props.material_index,
+ 'use_interior_vgroup': fracture_cell_props.use_interior_vgroup,
+
+ 'use_collection': fracture_cell_props.use_collection,
+ 'new_collection': fracture_cell_props.new_collection,
+ 'collection_name': fracture_cell_props.collection_name,
+ 'original_hide': fracture_cell_props.original_hide,
+ 'cell_relocate': fracture_cell_props.cell_relocate,
+ 'use_mass': fracture_cell_props.use_mass,
+ 'mass_name': fracture_cell_props.mass_name,
+ 'mass_mode': fracture_cell_props.mass_mode,
+ 'mass': fracture_cell_props.mass,
+
+ 'use_debug_points': fracture_cell_props.use_debug_points,
+ 'use_debug_redraw': fracture_cell_props.use_debug_redraw,
+ 'use_debug_bool': fracture_cell_props.use_debug_bool
+ }
+ return cell_keywords