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:
authorCampbell Barton <ideasman42@gmail.com>2019-07-07 04:19:05 +0300
committerCampbell Barton <ideasman42@gmail.com>2019-07-07 04:25:19 +0300
commit3f68f8442a531ebf234ac6d01fb35a4a5a0c2213 (patch)
treedfbe21b25f5cf0e527e3ca79972dca07db1b4a46 /object_fracture_cell
parent8f4c893499ffc6050d587ab03192e5457eed96f4 (diff)
Revert changes to 'object_fracture_cell'
This reverts commits: daaff4199bb8a9bcf90319e48f4444989a3fc42a. 64d89567923df223f50eeeaa2f79f93f23da74f6. fffaf5d2759d38d4166f608eab8871fcd59a7e11. f7c91d3382ea19ce4565105c85288044a2d1b833. These changes removed/added functionality beyond the scope of porting 2.7x to 2.8x, where only minor changes were needed.
Diffstat (limited to 'object_fracture_cell')
-rw-r--r--object_fracture_cell/__init__.py682
-rw-r--r--object_fracture_cell/fracture_cell_calc.py120
-rw-r--r--object_fracture_cell/fracture_cell_setup.py459
-rw-r--r--object_fracture_cell/operator.py284
-rw-r--r--object_fracture_cell/process/cell_calc.py149
-rw-r--r--object_fracture_cell/process/cell_functions.py601
-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.blendbin273382 -> 0 bytes
-rw-r--r--object_fracture_cell/utilities.py42
11 files changed, 992 insertions, 1776 deletions
diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py
index 794c2fe1..e1fcb41f 100644
--- a/object_fracture_cell/__init__.py
+++ b/object_fracture_cell/__init__.py
@@ -18,23 +18,20 @@
bl_info = {
"name": "Cell Fracture",
- "author": "ideasman42, phymec, Sergey Sharybin, Nobuyuki Hirakata",
- "version": (1, 0, 2),
- "blender": (2, 80, 0),
- "location": "View3D > Sidebar > Transform tab",
- "description": "Fractured Object, or Cracked Surface",
- "warning": "Work in Progress",
+ "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(operator)
-
-else:
- from . import operator
+#if "bpy" in locals():
+# import importlib
+# importlib.reload(fracture_cell_setup)
import bpy
from bpy.props import (
@@ -44,114 +41,244 @@ from bpy.props import (
FloatProperty,
FloatVectorProperty,
EnumProperty,
- BoolVectorProperty,
- PointerProperty,
)
-from bpy.types import (
- Panel,
- PropertyGroup,
- )
+from bpy.types import Operator
+def main_object(context, obj, level, **kw):
+ import random
-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 = "Create"
- bl_context = 'objectmode'
- bl_options = {"DEFAULT_CLOSED"}
+ # 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'}
- 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: 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,
)
- '''
- # -------------------------------------------------------------------------
- # 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",
@@ -159,51 +286,40 @@ class FractureCellProperties(PropertyGroup):
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,
+ min=0, max=5000,
default=0,
)
+
recursion_source_limit: IntProperty(
- name="Fracture Each",
+ name="Source Limit",
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.
+ min=0, max=5000,
default=8,
)
+
recursion_clamp: IntProperty(
- name="Max Fracture",
+ 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="Rec Chance",
+ name="Random Factor",
description="Likelihood of recursion",
min=0.0, max=1.0,
- default=1.00,
+ default=0.25,
)
+
recursion_chance_select: EnumProperty(
- name="Target",
+ name="Recurse Over",
items=(('RANDOM', "Random", ""),
('SIZE_MIN', "Small", "Recursively subdivide smaller objects"),
('SIZE_MAX', "Big", "Recursively subdivide bigger objects"),
@@ -212,77 +328,61 @@ class FractureCellProperties(PropertyGroup):
),
default='SIZE_MIN',
)
+
# -------------------------------------------------------------------------
- # Interior Meshes Options
+ # Mesh Data Options
+
use_smooth_faces: BoolProperty(
name="Smooth Faces",
- description="Smooth Faces of inner side",
default=False,
)
+
use_sharp_edges: BoolProperty(
- name="Mark Sharp Edges",
+ name="Sharp Edges",
description="Set sharp edges when disabled",
- default=False,
+ default=True,
)
+
use_sharp_edges_apply: BoolProperty(
- name="Edge Split Modifier",
- description="Add edge split modofier for sharp edges",
- default=False,
+ name="Apply Split Edge",
+ description="Split sharp hard edges",
+ default=True,
)
+
use_data_match: BoolProperty(
- name="Copy Original Data",
+ 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="Interior Material Slot",
+ name="Material",
description="Material index for interior faces",
default=0,
)
+
use_interior_vgroup: BoolProperty(
- name="Vertex Group",
+ name="Interior VGroup",
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="Use New",
- 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",
- )
+ # Physics Options
+
mass_mode: EnumProperty(
name="Mass Mode",
items=(('VOLUME', "Volume", "Objects get part of specified mass based on their volume"),
@@ -290,12 +390,55 @@ class FractureCellProperties(PropertyGroup):
),
default='VOLUME',
)
+
mass: FloatProperty(
- name="Mass Factor",
+ 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(
@@ -307,7 +450,7 @@ class FractureCellProperties(PropertyGroup):
use_debug_redraw: BoolProperty(
name="Show Progress Realtime",
description="Redraw as fracture is done",
- default=False,
+ default=True,
)
use_debug_bool: BoolProperty(
@@ -316,109 +459,110 @@ class FractureCellProperties(PropertyGroup):
default=False,
)
+ def execute(self, context):
+ keywords = self.as_keywords() # ignore=("blah",)
-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
- )
+ main(context, **keywords)
+ return {'FINISHED'}
-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 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():
- 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
- )
+ bpy.utils.register_class(FractureCell)
+ bpy.types.VIEW3D_PT_tools_object.append(menu_func)
+
def unregister():
- del bpy.types.WindowManager.fracture_material_props
- del bpy.types.WindowManager.fracture_crack_props
- del bpy.types.WindowManager.fracture_cell_props
+ bpy.utils.unregister_class(FractureCell)
+ bpy.types.VIEW3D_PT_tools_object.remove(menu_func)
- from bpy.utils import unregister_class
- for cls in reversed(classes):
- unregister_class(cls)
if __name__ == "__main__":
- register()
-
+ register()
diff --git a/object_fracture_cell/fracture_cell_calc.py b/object_fracture_cell/fracture_cell_calc.py
new file mode 100644
index 00000000..9e6f0de5
--- /dev/null
+++ b/object_fracture_cell/fracture_cell_calc.py
@@ -0,0 +1,120 @@
+# ##### 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
new file mode 100644
index 00000000..fcafa247
--- /dev/null
+++ b/object_fracture_cell/fracture_cell_setup.py
@@ -0,0 +1,459 @@
+# ##### 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()
diff --git a/object_fracture_cell/operator.py b/object_fracture_cell/operator.py
deleted file mode 100644
index c01e866a..00000000
--- a/object_fracture_cell/operator.py
+++ /dev/null
@@ -1,284 +0,0 @@
-
-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'}
-
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return obj and obj.type == "MESH"
-
- 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=350)
-
- 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_vert_child")
- row = col.row()
- row.prop(cell_props, "source_particle_own")
- row.prop(cell_props, "source_particle_child")
- row = col.row()
- row.prop(cell_props, "source_random")
- 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 = col.row(align=True)
- row.prop(cell_props, "margin")
- row.prop(cell_props, "use_recenter")
- row = col.row(align=True)
- row.prop(cell_props, "cell_scale")
- # 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.prop(cell_props, "recursion")
- row.prop(cell_props, "recursion_chance")
- row = col.row(align=True)
- 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 = col.row()
- 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.prop(cell_props, "use_data_match")
- row.prop(cell_props, "use_interior_vgroup")
- row = col.row(align=True)
- 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="Object Management")
- row = col.row(align=True)
- row.prop(cell_props, "original_hide")
- row.prop(cell_props, "cell_relocate")
-
- box = layout.box()
- col = box.column()
- col.label(text="Collections:")
- 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_points")
- row.prop(cell_props, "use_debug_bool")
- row = col.row(align=True)
- row.prop(cell_props, "use_debug_redraw")
-
-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'}
-
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return obj and obj.type == "MESH"
-
- def invoke(self, context, event):
- wm = context.window_manager
- return wm.invoke_props_dialog(self, width=350)
-
- 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
deleted file mode 100644
index 9541e5f5..00000000
--- a/object_fracture_cell/process/cell_calc.py
+++ /dev/null
@@ -1,149 +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_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
deleted file mode 100644
index 026bdc53..00000000
--- a/object_fracture_cell/process/cell_functions.py
+++ /dev/null
@@ -1,601 +0,0 @@
-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):
- mod = cell.modifiers.new(name="Boolean", type='BOOLEAN')
- mod.object = original
- mod.operation = 'INTERSECT'
-
- 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.object.modifier_apply(apply_as='DATA', modifier="Boolean")
-
- 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
-
- 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
deleted file mode 100644
index eefa18c9..00000000
--- a/object_fracture_cell/process/cell_main.py
+++ /dev/null
@@ -1,208 +0,0 @@
-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
deleted file mode 100644
index ffc513b6..00000000
--- a/object_fracture_cell/process/crack_functions.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# 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
deleted file mode 100644
index a2b2d803..00000000
--- a/object_fracture_cell/process/material_functions.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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
deleted file mode 100644
index df865ee8..00000000
--- a/object_fracture_cell/process/materials/materials1.blend
+++ /dev/null
Binary files differ
diff --git a/object_fracture_cell/utilities.py b/object_fracture_cell/utilities.py
deleted file mode 100644
index 0e87a967..00000000
--- a/object_fracture_cell/utilities.py
+++ /dev/null
@@ -1,42 +0,0 @@
-
-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