Update for changes in Blender's API
[blender-addons.git] / object_fracture_crack / crack_it.py
blob489d2c8787af2007bd6715341bf32373abf7ca19
1 # gpl: author Nobuyuki Hirakata
3 import bpy
5 import bmesh
6 from random import (
7 gauss,
8 seed,
10 from math import radians
11 from mathutils import Euler
14 # Allow changing the original material names from the .blend file
15 # by replacing them with the UI Names from the EnumProperty
16 def get_ui_mat_name(mat_name):
17 mat_ui_name = "CrackIt Material"
18 try:
19 # access the Scene type directly to get the name from the enum
20 mat_items = bpy.types.Scene.crackit[1]["type"].bl_rna.material_preset[1]["items"]
21 for mat_id, mat_list in enumerate(mat_items):
22 if mat_name in mat_list:
23 mat_ui_name = mat_items[mat_id][1]
24 break
25 del mat_items
26 except Exception as e:
27 error_handlers(
28 False, "get_ui_mat_name", e,
29 "Retrieving the EnumProperty key UI Name could not be completed", True
31 pass
33 return mat_ui_name
36 def error_handlers(self, op_name, error, reports="ERROR", func=False):
37 if self and reports:
38 self.report({'WARNING'}, reports + " (See Console for more info)")
40 is_func = "Function" if func else "Operator"
41 print("\n[Cell Fracture Crack It]\n{}: {}\nError: "
42 "{}\nReport: {}\n".format(is_func, op_name, error, reports))
45 # -------------------- Crack -------------------
46 # Cell fracture and post-process:
47 def makeFracture(child_verts=False, division=100, noise=0.00,
48 scaleX=1.00, scaleY=1.00, scaleZ=1.00, recursion=0, margin=0.001):
50 # Get active object name and active layer
51 active_name = bpy.context.scene.objects.active.name
52 active_layer = bpy.context.scene.active_layer
54 # source method of whether use child verts
55 if child_verts is True:
56 crack_source = 'VERT_CHILD'
57 else:
58 crack_source = 'PARTICLE_OWN'
60 bpy.ops.object.add_fracture_cell_objects(
61 source={crack_source}, source_limit=division, source_noise=noise,
62 cell_scale=(scaleX, scaleY, scaleZ), recursion=recursion,
63 recursion_source_limit=8, recursion_clamp=250, recursion_chance=0.25,
64 recursion_chance_select='SIZE_MIN', use_smooth_faces=False,
65 use_sharp_edges=False, use_sharp_edges_apply=True, use_data_match=True,
66 use_island_split=True, margin=margin, material_index=0,
67 use_interior_vgroup=False, mass_mode='VOLUME', mass=1, use_recenter=True,
68 use_remove_original=True, use_layer_index=0, use_layer_next=False,
69 group_name="", use_debug_points=False, use_debug_redraw=True, use_debug_bool=False
72 _makeJoin(active_name, active_layer)
75 # Join fractures into an object
76 def _makeJoin(active_name, active_layer):
77 # Get object by name
78 bpy.ops.object.select_all(action='DESELECT')
79 bpy.ops.object.select_pattern(pattern=active_name + '_cell*')
80 fractures = bpy.context.selected_objects
82 if fractures:
83 # Execute join
84 bpy.context.scene.objects.active = fractures[0]
85 fractures[0].select = True
86 bpy.ops.object.join()
87 else:
88 error_handlers(
89 False, "_makeJoin", "if fractures condition has not passed",
90 "Warning: No objects could be joined", True
93 # Change name
94 bpy.context.scene.objects.active.name = active_name + '_crack'
96 # Change origin
97 bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
100 # Add modifier and setting
101 def addModifiers():
102 bpy.ops.object.modifier_add(type='DECIMATE')
103 decimate = bpy.context.object.modifiers[-1]
104 decimate.name = 'DECIMATE_crackit'
105 decimate.ratio = 0.4
107 bpy.ops.object.modifier_add(type='SUBSURF')
108 subsurf = bpy.context.object.modifiers[-1]
109 subsurf.name = 'SUBSURF_crackit'
111 bpy.ops.object.modifier_add(type='SMOOTH')
112 smooth = bpy.context.object.modifiers[-1]
113 smooth.name = 'SMOOTH_crackit'
116 # -------------- multi extrude --------------------
117 # var1=random offset, var2=random rotation, var3=random scale
118 def multiExtrude(off=0.1, rotx=0, roty=0, rotz=0, sca=1.0,
119 var1=0.01, var2=0.3, var3=0.3, num=1, ran=0):
121 obj = bpy.context.object
122 bpy.context.tool_settings.mesh_select_mode = [False, False, True]
124 # bmesh operations
125 bpy.ops.object.mode_set()
126 bm = bmesh.new()
127 bm.from_mesh(obj.data)
128 sel = [f for f in bm.faces if f.select]
130 # faces loop
131 for i, of in enumerate(sel):
132 rot = _vrot(r=i, ran=ran, rotx=rotx, var2=var2, roty=roty, rotz=rotz)
133 off = _vloc(r=i, ran=ran, off=off, var1=var1)
134 of.normal_update()
136 # extrusion loop
137 for r in range(num):
138 nf = of.copy()
139 nf.normal_update()
140 no = nf.normal.copy()
141 ce = nf.calc_center_bounds()
142 s = _vsca(r=i + r, ran=ran, var3=var3, sca=sca)
144 for v in nf.verts:
145 v.co -= ce
146 v.co.rotate(rot)
147 v.co += ce + no * off
148 v.co = v.co.lerp(ce, 1 - s)
150 # extrude code from TrumanBlending
151 for a, b in zip(of.loops, nf.loops):
152 sf = bm.faces.new((a.vert, a.link_loop_next.vert,
153 b.link_loop_next.vert, b.vert))
154 sf.normal_update()
156 bm.faces.remove(of)
157 of = nf
159 for v in bm.verts:
160 v.select = False
162 for e in bm.edges:
163 e.select = False
165 bm.to_mesh(obj.data)
166 obj.data.update()
169 def _vloc(r, ran, off, var1):
170 seed(ran + r)
171 return off * (1 + gauss(0, var1 / 3))
174 def _vrot(r, ran, rotx, var2, roty, rotz):
175 seed(ran + r)
176 return Euler((radians(rotx) + gauss(0, var2 / 3),
177 radians(roty) + gauss(0, var2 / 3),
178 radians(rotz) + gauss(0, var2 / 3)), 'XYZ')
181 def _vsca(r, ran, sca, var3):
182 seed(ran + r)
183 return sca * (1 + gauss(0, var3 / 3))
186 # Centroid of a selection of vertices
187 def _centro(ver):
188 vvv = [v for v in ver if v.select]
189 if not vvv or len(vvv) == len(ver):
190 return ('error')
192 x = sum([round(v.co[0], 4) for v in vvv]) / len(vvv)
193 y = sum([round(v.co[1], 4) for v in vvv]) / len(vvv)
194 z = sum([round(v.co[2], 4) for v in vvv]) / len(vvv)
196 return (x, y, z)
199 # Retrieve the original state of the object
200 def _volver(obj, copia, om, msm, msv):
201 for i in copia:
202 obj.data.vertices[i].select = True
203 bpy.context.tool_settings.mesh_select_mode = msm
205 for i in range(len(msv)):
206 obj.modifiers[i].show_viewport = msv[i]
209 # -------------- Material preset --------------------------
210 def appendMaterial(addon_path, material_name, mat_ui_names="Nameless Material"):
211 # Load material from the addon directory
212 file_path = _makeFilePath(addon_path=addon_path)
213 bpy.ops.wm.append(filename=material_name, directory=file_path)
215 # If material is loaded some times, select the last-loaded material
216 last_material = _getAppendedMaterial(material_name)
218 if last_material:
219 mat = bpy.data.materials[last_material]
220 # skip renaming if the prop is True
221 if not bpy.context.scene.crackit.material_lib_name:
222 mat.name = mat_ui_names
224 # Apply Only one material in the material slot
225 for m in bpy.context.object.data.materials:
226 bpy.ops.object.material_slot_remove()
228 bpy.context.object.data.materials.append(mat)
230 return True
232 return False
235 # Make file path of addon
236 def _makeFilePath(addon_path):
237 material_folder = "/materials"
238 blend_file = "/materials1.blend"
239 category = "\\Material\\"
241 file_path = addon_path + material_folder + blend_file + category
242 return file_path
245 # Get last-loaded material, such as ~.002
246 def _getAppendedMaterial(material_name):
247 # Get material name list
248 material_names = [m.name for m in bpy.data.materials if material_name in m.name]
249 if material_names:
250 # Return last material in the sorted order
251 material_names.sort()
253 return material_names[-1]
255 return None