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