|
| 1 | +#region IMPORTS |
| 2 | +import bpy |
| 3 | +import random |
| 4 | +import os |
| 5 | +# from randomizetexture import MAIN_OBJ_NAME |
| 6 | +#endregion |
| 7 | + |
| 8 | +#region CONSTANTS |
| 9 | +DEFECTS_PER_PART = 101 |
| 10 | +DEFECT_NAME = 'Sphere' |
| 11 | +BLOWHOLE_ID = 2 |
| 12 | +MAIN_OBJ_NAME = 'Turbosupercharger' |
| 13 | + |
| 14 | +#cloud noise texture randomization values |
| 15 | +CLOUD_TXTR_MIN_NOISE_STR = -0.8 |
| 16 | +CLOUD_TXTR_MAX_NOISE_STR = 0.8 |
| 17 | +CLOUD_TXTR_MIN_NOISE_SCALE = 2 |
| 18 | +CLOUD_TXTR_MAX_NOISE_SCALE = 10 |
| 19 | +CLOUD_TXTR_MIN_NOISE_DEPTH = 0 |
| 20 | +CLOUD_TXTR_MAX_NOISE_DEPTH = 20 |
| 21 | +#endregion |
| 22 | + |
| 23 | +#region OBJECTS |
| 24 | +def get_active_object(): |
| 25 | + return bpy.context.active_object |
| 26 | + |
| 27 | +def get_object(ref=None): |
| 28 | + objref = None |
| 29 | + if ref is None: |
| 30 | + objref = get_active_object() |
| 31 | + else: |
| 32 | + if type(ref) == str: |
| 33 | + objref = bpy.data.objects[ref] |
| 34 | + else: |
| 35 | + objref = ref |
| 36 | + return objref |
| 37 | + |
| 38 | +def copy_object(tocopy, col = None): |
| 39 | + new_obj = None |
| 40 | + to_copy = get_object(tocopy) |
| 41 | + col_ref = None |
| 42 | + |
| 43 | + if col == None: |
| 44 | + col_ref = get_active_collection() |
| 45 | + elif type(col) == str: |
| 46 | + if collection_exists(col): |
| 47 | + col_ref = col |
| 48 | + else: |
| 49 | + col_ref = create_collection(col) |
| 50 | + else: |
| 51 | + col_ref = col |
| 52 | + |
| 53 | + new_obj = to_copy.copy() |
| 54 | + if new_obj.data is not None: |
| 55 | + new_obj.data = to_copy.data.copy() |
| 56 | + col_ref.objects.link(new_obj) |
| 57 | + return new_obj |
| 58 | + |
| 59 | +#endregion |
| 60 | + |
| 61 | +#region MESHES |
| 62 | +def get_vertices(ref): |
| 63 | + if type(ref) == str: |
| 64 | + return get_object(ref).data.vertices |
| 65 | + else: |
| 66 | + return ref.data.vertices |
| 67 | +#endregion |
| 68 | + |
| 69 | +#region COLLECTIONS |
| 70 | +def get_active_collection(): |
| 71 | + return bpy.context.view_layer.active_layer_collection.collection |
| 72 | + |
| 73 | +def collection_exists(col): |
| 74 | + if type(col) == str: |
| 75 | + return col in bpy.data.collections |
| 76 | + return col.name in bpy.data.collections |
| 77 | + |
| 78 | +def create_collection(name): |
| 79 | + bpy.data.collections.new(name) |
| 80 | + colref = bpy.data.collections[name] |
| 81 | + bpy.context.scene.collection.children.link(colref) |
| 82 | + return colref |
| 83 | +#endregion |
| 84 | + |
| 85 | +#region MODIFIERS |
| 86 | +def make_solid(ref = None, tar_thickness = 0.01): |
| 87 | + target = get_object(ref) |
| 88 | + tar_mods = bpy.data.objects[target.name].modifiers |
| 89 | + if any([m for m in tar_mods if m.name == "Solid"]): |
| 90 | + change_thickness(target, tar_thickness) |
| 91 | + else: |
| 92 | + solid_mod = tar_mods.new(name='Solid', type ='SOLIDIFY') |
| 93 | + solid_mod.thickness = tar_thickness |
| 94 | + |
| 95 | +#doesn't work as expected: setting thickness to 0 makes the object hollow! |
| 96 | +def change_thickness(ref=None, thickness = 0.001): |
| 97 | + target = get_object(ref) |
| 98 | + #this is not a great solution, since the mod name is hard coded |
| 99 | + bpy.data.objects[target.name].modifiers['Solid'].thickness = thickness |
| 100 | + |
| 101 | +def make_boolean_difference(main_ref = None, tool_ref = None): |
| 102 | + main_obj = get_object(main_ref) |
| 103 | + tool_obj = get_object(tool_ref) |
| 104 | + bool_mod = main_obj.modifiers.new(name='Bool_Diff', type='BOOLEAN') |
| 105 | + bool_mod.object = tool_obj |
| 106 | + bool_mod.operation = 'DIFFERENCE' |
| 107 | + #TODO: could add if condition here (don't want to apply immediately) |
| 108 | + bpy.ops.object.modifier_apply({"object": main_obj}, modifier=bool_mod.name) |
| 109 | + |
| 110 | +def make_boolean_intersection(main_ref = None, tool_ref = None): |
| 111 | + main_obj = get_object(main_ref) |
| 112 | + tool_obj = get_object(tool_ref) |
| 113 | + bool_mod = main_obj.modifiers.new(name='Bool_Inter', type='BOOLEAN') |
| 114 | + bool_mod.object = tool_obj |
| 115 | + bool_mod.operation = 'INTERSECT' |
| 116 | + #TODO: could add if condition here (don't want to apply immediately) |
| 117 | + bpy.ops.object.modifier_apply({"object": main_obj}, modifier=bool_mod.name) |
| 118 | + |
| 119 | +def add_noisy_displacement(main_ref = None, strength = 0, noise_depth = 4, noise_scale = 6): |
| 120 | + main_obj = get_object(main_ref) |
| 121 | + displace = main_obj.modifiers.new(name="Displace", type='DISPLACE') |
| 122 | + displace.texture = add_noise_texture(noise_depth, noise_scale) |
| 123 | + displace.strength = strength |
| 124 | +#endregion |
| 125 | + |
| 126 | +#region TEXTURES |
| 127 | +def add_noise_texture(noise_depth = 4, noise_scale = 6): |
| 128 | + noise_texture = bpy.data.textures.new("Noise", 'CLOUDS') |
| 129 | + noise_texture.noise_basis = 'BLENDER_ORIGINAL' |
| 130 | + noise_texture.noise_type = 'SOFT_NOISE' |
| 131 | + noise_texture.noise_depth = noise_depth |
| 132 | + noise_texture.noise_scale = noise_scale |
| 133 | + return noise_texture |
| 134 | +#endregion |
| 135 | + |
| 136 | +#region CUSTOM_METHODS |
| 137 | +def create_defect(main_obj_ref, tool_ref, vert, category_id = -1): |
| 138 | + tool_ref.location = vert.co - random.uniform(0,0.3)*vert.normal |
| 139 | + |
| 140 | + #TODO: move the randomization elsewhere |
| 141 | + noise_strength = random.uniform(CLOUD_TXTR_MIN_NOISE_STR, CLOUD_TXTR_MAX_NOISE_STR) |
| 142 | + noise_depth = int(random.uniform(CLOUD_TXTR_MIN_NOISE_DEPTH, CLOUD_TXTR_MAX_NOISE_DEPTH)) |
| 143 | + noise_scale = random.uniform(CLOUD_TXTR_MIN_NOISE_SCALE, CLOUD_TXTR_MAX_NOISE_SCALE) |
| 144 | + add_noisy_displacement(tool_ref, noise_strength, noise_depth, noise_scale) |
| 145 | + |
| 146 | + main_obj_copy = copy_object(main_obj_ref) |
| 147 | + add_category_id(main_obj_copy,category_id) |
| 148 | + |
| 149 | + make_boolean_difference(main_obj_ref,tool_ref) |
| 150 | + |
| 151 | + make_solid(tool_ref) |
| 152 | + |
| 153 | + make_boolean_intersection(main_obj_copy, tool_ref) |
| 154 | + tool_ref.modifiers.clear() |
| 155 | + |
| 156 | +def select_defect_verts(mo_vertices, number_of_defects=DEFECTS_PER_PART): |
| 157 | + vertex_count = len(mo_vertices) |
| 158 | + #avoid intersecting defects |
| 159 | + step_size = int(vertex_count/(1.3*number_of_defects)) |
| 160 | + starting_vert_number = random.randint(0,step_size) |
| 161 | + v_index = starting_vert_number |
| 162 | + defect_vert_indices = [] |
| 163 | + while(v_index + step_size < (1/1.3)*vertex_count): |
| 164 | + defect_vert_indices.append(v_index) |
| 165 | + v_index += step_size |
| 166 | + return defect_vert_indices |
| 167 | + |
| 168 | +def add_category_id(obj_ref, category_id = -1): |
| 169 | + obj_ref['category_id'] = category_id |
| 170 | +#endregion |
| 171 | + |
| 172 | +#region CREATE DEFECTS |
| 173 | + |
| 174 | +#object names must match names in the .blend file |
| 175 | +main_obj_ref = get_object(MAIN_OBJ_NAME) |
| 176 | + |
| 177 | +tool_ref = get_object(DEFECT_NAME) |
| 178 | + |
| 179 | +mo_vertices = get_vertices(main_obj_ref) |
| 180 | +defect_vert_indices = select_defect_verts(mo_vertices) |
| 181 | + |
| 182 | +for v in defect_vert_indices: |
| 183 | + create_defect(main_obj_ref, tool_ref, mo_vertices[v], BLOWHOLE_ID) |
| 184 | + |
| 185 | +#remove defect tool |
| 186 | +bpy.ops.object.select_all(action='DESELECT') |
| 187 | +bpy.data.objects[DEFECT_NAME].select_set(True) |
| 188 | +bpy.ops.object.delete() |
| 189 | + |
| 190 | +#smooth objects to remove artifacts |
| 191 | +ctx = bpy.context.copy() |
| 192 | +all_objects = [o for o in bpy.data.objects if o.type == "MESH"] |
| 193 | +o = all_objects[0] |
| 194 | +ctx['object'] = o |
| 195 | +ctx['active_object'] = o |
| 196 | +ctx['selected_objects'] = all_objects |
| 197 | +ctx['selected_editable_objects'] = all_objects |
| 198 | +bpy.ops.object.shade_smooth(ctx) |
| 199 | +for m in bpy.data.meshes: |
| 200 | + m.use_auto_smooth = True |
| 201 | + |
| 202 | + |
| 203 | +#save the scene |
| 204 | +wd = os.getcwd() |
| 205 | +scene_path = os.path.join(wd,'scene.blend') |
| 206 | +print("Defects were created. Will now be saved") |
| 207 | +bpy.ops.wm.save_mainfile(filepath=scene_path) |
| 208 | + |
| 209 | +#endregion |
0 commit comments