diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-07-07 04:19:05 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-07-07 04:25:19 +0300 |
commit | 3f68f8442a531ebf234ac6d01fb35a4a5a0c2213 (patch) | |
tree | dfbe21b25f5cf0e527e3ca79972dca07db1b4a46 | |
parent | 8f4c893499ffc6050d587ab03192e5457eed96f4 (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.
-rw-r--r-- | object_fracture_cell/__init__.py | 682 | ||||
-rw-r--r-- | object_fracture_cell/fracture_cell_calc.py | 120 | ||||
-rw-r--r-- | object_fracture_cell/fracture_cell_setup.py | 459 | ||||
-rw-r--r-- | object_fracture_cell/operator.py | 284 | ||||
-rw-r--r-- | object_fracture_cell/process/cell_calc.py | 149 | ||||
-rw-r--r-- | object_fracture_cell/process/cell_functions.py | 601 | ||||
-rw-r--r-- | object_fracture_cell/process/cell_main.py | 208 | ||||
-rw-r--r-- | object_fracture_cell/process/crack_functions.py | 140 | ||||
-rw-r--r-- | object_fracture_cell/process/material_functions.py | 83 | ||||
-rw-r--r-- | object_fracture_cell/process/materials/materials1.blend | bin | 273382 -> 0 bytes | |||
-rw-r--r-- | object_fracture_cell/utilities.py | 42 | ||||
-rw-r--r-- | object_fracture_crack/__init__.py | 148 | ||||
-rw-r--r-- | object_fracture_crack/crack_it.py | 255 | ||||
-rw-r--r-- | object_fracture_crack/materials/materials1.blend | bin | 0 -> 237466 bytes | |||
-rw-r--r-- | object_fracture_crack/operator.py | 164 |
15 files changed, 1559 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 Binary files differdeleted file mode 100644 index df865ee8..00000000 --- a/object_fracture_cell/process/materials/materials1.blend +++ /dev/null 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 diff --git a/object_fracture_crack/__init__.py b/object_fracture_crack/__init__.py new file mode 100644 index 00000000..3e3ec838 --- /dev/null +++ b/object_fracture_crack/__init__.py @@ -0,0 +1,148 @@ +# ##### 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 Crack It", + "author": "Nobuyuki Hirakata", + "version": (0, 1, 2), + "blender": (2, 78, 5), + "location": "View3D > Toolshelf > Create Tab", + "description": "Displaced Cell Fracture Addon", + "warning": "Make sure to enable 'Object: Cell Fracture' Addon", + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/" + "Py/Scripts/Object/CrackIt", + "category": "Object" +} + +if 'bpy' in locals(): + import importlib + importlib.reload(operator) + +else: + from . import operator + +import bpy +from bpy.types import PropertyGroup +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + PointerProperty, + ) +import os + + +class CrackItProperties(PropertyGroup): + # Input on toolshelf before execution + # In Panel subclass, In bpy.types.Operator subclass, + # reference them by context.scene.crackit + + fracture_childverts: BoolProperty( + name="From Child Verts", + description="Use child object's vertices and position for origin of crack", + default=False + ) + fracture_scalex: FloatProperty( + name="Scale X", + description="Scale X", + default=1.00, + min=0.00, + max=1.00 + ) + fracture_scaley: FloatProperty( + name="Scale Y", + description="Scale Y", + default=1.00, + min=0.00, + max=1.00 + ) + fracture_scalez: FloatProperty( + name="Scale Z", + description="Scale Z", + default=1.00, + min=0.00, + max=1.00 + ) + fracture_div: IntProperty( + name="Max Crack", + description="Max Crack", + default=100, + min=0, + max=10000 + ) + fracture_margin: FloatProperty( + name="Margin Size", + description="Margin Size", + default=0.001, + min=0.000, + max=1.000 + ) + extrude_offset: FloatProperty( + name="Offset", + description="Extrude Offset", + default=0.10, + min=0.00, + max=2.00 + ) + extrude_random: FloatProperty( + name="Random", + description="Extrude Random", + default=0.30, + min=-1.00, + max=1.00 + ) + # Path of the addon + material_addonpath = os.path.dirname(__file__) + # Selection of material preset + # 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_mud1', "Mud", "Mud material"), + ('crackit_tree1_moss1', "Tree Moss", "Tree Material"), + ('crackit_tree2_dry1', "Tree Dry", "Tree Material"), + ('crackit_tree3_red1', "Tree Red", "Tree Material"), + ('crackit_rock1', "Rock", "Rock 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 + ) + + +def register(): + bpy.utils.register_module(__name__) + bpy.types.Scene.crackit = PointerProperty( + type=CrackItProperties + ) + + +def unregister(): + del bpy.types.Scene.crackit + bpy.utils.unregister_module(__name__) + + +if __name__ == "__main__": + register() diff --git a/object_fracture_crack/crack_it.py b/object_fracture_crack/crack_it.py new file mode 100644 index 00000000..68857908 --- /dev/null +++ b/object_fracture_crack/crack_it.py @@ -0,0 +1,255 @@ +# gpl: author Nobuyuki Hirakata + +import bpy + +import bmesh +from random import ( + gauss, + seed, + ) +from math import radians +from mathutils import Euler + + +# 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 + + +def error_handlers(self, op_name, error, reports="ERROR", func=False): + if self and reports: + self.report({'WARNING'}, 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)) + + +# -------------------- Crack ------------------- +# Cell fracture and post-process: +def makeFracture(child_verts=False, division=100, noise=0.00, + scaleX=1.00, scaleY=1.00, scaleZ=1.00, recursion=0, margin=0.001): + + # Get active object name and active layer + active_name = bpy.context.view_layer.objects.active.name + active_layer = bpy.context.scene.active_layer + + # source method of whether use child verts + if child_verts is True: + crack_source = 'VERT_CHILD' + else: + crack_source = 'PARTICLE_OWN' + + bpy.ops.object.add_fracture_cell_objects( + source={crack_source}, source_limit=division, source_noise=noise, + cell_scale=(scaleX, scaleY, scaleZ), recursion=recursion, + recursion_source_limit=8, recursion_clamp=250, recursion_chance=0.25, + recursion_chance_select='SIZE_MIN', use_smooth_faces=False, + use_sharp_edges=False, use_sharp_edges_apply=True, use_data_match=True, + use_island_split=True, margin=margin, material_index=0, + use_interior_vgroup=False, mass_mode='VOLUME', mass=1, use_recenter=True, + use_remove_original=True, use_layer_index=0, use_layer_next=False, + group_name="", use_debug_points=False, use_debug_redraw=True, use_debug_bool=False + ) + + _makeJoin(active_name, active_layer) + + +# Join fractures into an object +def _makeJoin(active_name, active_layer): + # Get object by name + bpy.ops.object.select_all(action='DESELECT') + bpy.ops.object.select_pattern(pattern=active_name + '_cell*') + fractures = bpy.context.selected_objects + + if fractures: + # Execute join + bpy.context.view_layer.objects.active = fractures[0] + fractures[0].select_set(True) + bpy.ops.object.join() + else: + error_handlers( + False, "_makeJoin", "if fractures condition has not passed", + "Warning: No objects could be joined", True + ) + + # Change name + bpy.context.view_layer.objects.active.name = active_name + '_crack' + + # Change origin + bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN') + + +# Add modifier and setting +def addModifiers(): + bpy.ops.object.modifier_add(type='DECIMATE') + decimate = bpy.context.object.modifiers[-1] + decimate.name = 'DECIMATE_crackit' + decimate.ratio = 0.4 + + 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' + + +# -------------- multi extrude -------------------- +# var1=random offset, var2=random rotation, var3=random scale +def multiExtrude(off=0.1, rotx=0, roty=0, rotz=0, sca=1.0, + var1=0.01, var2=0.3, var3=0.3, 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] + + +# -------------- Material preset -------------------------- +def appendMaterial(addon_path, material_name, mat_ui_names="Nameless Material"): + # Load material from the addon directory + file_path = _makeFilePath(addon_path=addon_path) + bpy.ops.wm.append(filename=material_name, directory=file_path) + + # If material is loaded some times, select the last-loaded material + last_material = _getAppendedMaterial(material_name) + + if last_material: + mat = bpy.data.materials[last_material] + # skip renaming if the prop is True + if not bpy.context.scene.crackit.material_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_crack/materials/materials1.blend b/object_fracture_crack/materials/materials1.blend Binary files differnew file mode 100644 index 00000000..969f2068 --- /dev/null +++ b/object_fracture_crack/materials/materials1.blend diff --git a/object_fracture_crack/operator.py b/object_fracture_crack/operator.py new file mode 100644 index 00000000..e0dbc5fd --- /dev/null +++ b/object_fracture_crack/operator.py @@ -0,0 +1,164 @@ +# gpl: author Nobuyuki Hirakata + +import bpy +from bpy.types import ( + Operator, + Panel, + ) +from . import crack_it + + +def check_object_cell_fracture(): + if "object_fracture_cell" in bpy.context.preferences.addons.keys(): + return True + return False + + +# Access by bpy.ops.mesh.crackit_fracture +class FractureOperation(Operator): + bl_idname = "mesh.crackit_fracture" + bl_label = "Crack it!" + bl_description = ("Make cracks using the cell fracture add-on\n" + "Needs only one Selected Mesh Object") + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + obj = context.active_object + sel_obj = len(context.selected_objects) == 1 + + return (obj is not None and obj.type == "MESH" and sel_obj) + + def execute(self, context): + if check_object_cell_fracture(): + crackit = context.scene.crackit + try: + crack_it.makeFracture( + child_verts=crackit.fracture_childverts, + division=crackit.fracture_div, scaleX=crackit.fracture_scalex, + scaleY=crackit.fracture_scaley, scaleZ=crackit.fracture_scalez, + margin=crackit.fracture_margin + ) + crack_it.addModifiers() + crack_it.multiExtrude( + off=crackit.extrude_offset, + var2=crackit.extrude_random, var3=crackit.extrude_random + ) + bpy.ops.object.shade_smooth() + + except Exception as e: + crack_it.error_handlers( + self, "mesh.crackit_fracture", e, "Crack It! could not be completed." + ) + return {"CANCELLED"} + else: + self.report({'WARNING'}, + "Depends on Object: Cell Fracture addon. Please enable it first. " + "Operation Cancelled" + ) + return {"CANCELLED"} + + return {'FINISHED'} + + +# Apply material preset +# Access by bpy.ops.mesh.crackit_material +class MaterialOperation(Operator): + bl_idname = "mesh.crackit_material" + bl_label = "Apply Material" + bl_description = ("Apply a preset material\n" + "The Material will be applied to the Active Object\n" + "from the type of Mesh, Curve, Surface, Font, Meta") + 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): + crackit = context.scene.crackit + mat_name = crackit.material_preset + mat_lib_name = crackit.material_lib_name + mat_ui_name = crack_it.get_ui_mat_name(mat_name) if not mat_lib_name else mat_name + + try: + crack_it.appendMaterial( + addon_path=crackit.material_addonpath, + material_name=mat_name, + mat_ui_names=mat_ui_name + ) + except Exception as e: + crack_it.error_handlers( + self, "mesh.crackit_material", e, + "The active Object could not have the Material {} applied".format(mat_ui_name) + ) + return {"CANCELLED"} + + return {'FINISHED'} + + +# Menu settings +class crackitPanel(Panel): + bl_label = "Crack it!" + bl_idname = 'crack_it' + bl_space_type = "VIEW_3D" + bl_region_type = "TOOLS" + bl_category = "Create" + bl_context = 'objectmode' + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + crackit = context.scene.crackit + layout = self.layout + + # Crack input + box = layout.box() + row = box.row() + + # Warning if the fracture cell addon is not enabled + if not check_object_cell_fracture(): + col = box.column() + col.label(text="Please enable Object: Cell Fracture addon", icon="INFO") + col.separator() + col.operator("preferences.addon_show", + text="Go to Cell Fracture addon", + icon="PREFERENCES").module = "object_fracture_cell" + + layout.separator() + return + else: + row.operator(FractureOperation.bl_idname, icon="SPLITSCREEN") + + row = box.row() + row.prop(crackit, "fracture_childverts") + + col = box.column(align=True) + col.prop(crackit, "fracture_scalex") + col.prop(crackit, "fracture_scaley") + col.prop(crackit, "fracture_scalez") + + col = box.column(align=True) + col.label(text="Settings:") + col.prop(crackit, "fracture_div") + col.prop(crackit, "fracture_margin") + + col = box.column(align=True) + col.label(text="Extrude:") + col.prop(crackit, "extrude_offset") + col.prop(crackit, "extrude_random") + + # material Preset: + box = layout.box() + row = box.row() + row.label(text="Material Preset:") + row_sub = row.row() + row_sub.prop(crackit, "material_lib_name", text="", + toggle=True, icon="LONGDISPLAY") + row = box.row() + row.prop(crackit, "material_preset") + + row = box.row() + row.operator(MaterialOperation.bl_idname) |