|
| 1 | +import bpy |
| 2 | +import random |
| 3 | +import os |
| 4 | + |
| 5 | +# Directory where the OBJ files will be saved |
| 6 | +export_directory = "/tmp/regolith/" # Change this to your desired path |
| 7 | + |
| 8 | +def create_granular_particle(): |
| 9 | + # Create a base shape (e.g., icosphere for a round particle) |
| 10 | + bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=0.5, location=(0, 0, 0)) # Generate at origin |
| 11 | + particle = bpy.context.object |
| 12 | + |
| 13 | + # First Displace Modifier for Coarse Roughness |
| 14 | + bpy.ops.object.modifier_add(type='DISPLACE') |
| 15 | + displace_mod = particle.modifiers[-1] |
| 16 | + |
| 17 | + # Create a texture for coarse displacement |
| 18 | + tex_name = "CoarseRoughTexture_" + str(random.randint(0, 10000)) |
| 19 | + tex = bpy.data.textures.new(name=tex_name, type='VORONOI') |
| 20 | + tex.noise_intensity = random.uniform(0.8, 1.2) |
| 21 | + tex.distance_metric = 'MINKOVSKY' |
| 22 | + tex.minkovsky_exponent = random.uniform(0.6, 0.9) |
| 23 | + |
| 24 | + displace_mod.texture = tex |
| 25 | + displace_mod.strength = random.uniform(0.1, 0.3) |
| 26 | + |
| 27 | + bpy.ops.object.modifier_apply(modifier=displace_mod.name) |
| 28 | + |
| 29 | + # Second Displace Modifier for Fine Surface Detail |
| 30 | + bpy.ops.object.modifier_add(type='DISPLACE') |
| 31 | + displace_mod2 = particle.modifiers[-1] |
| 32 | + tex_name2 = "FineRoughTexture_" + str(random.randint(0, 10000)) |
| 33 | + tex2 = bpy.data.textures.new(name=tex_name2, type='CLOUDS') |
| 34 | + tex2.noise_scale = random.uniform(0.1, 0.15) |
| 35 | + displace_mod2.texture = tex2 |
| 36 | + displace_mod2.strength = random.uniform(0.02, 0.08) |
| 37 | + |
| 38 | + bpy.ops.object.modifier_apply(modifier=displace_mod2.name) |
| 39 | + |
| 40 | + # Subdivision Surface Modifier for smoothing |
| 41 | + bpy.ops.object.modifier_add(type='SUBSURF') |
| 42 | + subsurf_mod = particle.modifiers[-1] |
| 43 | + subsurf_mod.levels = 1 |
| 44 | + bpy.ops.object.modifier_apply(modifier=subsurf_mod.name) |
| 45 | + |
| 46 | + # Random rotation for variety |
| 47 | + particle.rotation_euler = (random.uniform(0, 360), random.uniform(0, 360), random.uniform(0, 360)) |
| 48 | + |
| 49 | + # Randomize the shape: flat, elongated, or round |
| 50 | + shape_variation = random.choice(['round', 'flat', 'elongated']) |
| 51 | + |
| 52 | + if shape_variation == 'flat': |
| 53 | + particle.scale = (random.uniform(0.008, 0.012), |
| 54 | + random.uniform(0.008, 0.012), |
| 55 | + random.uniform(0.004, 0.006)) |
| 56 | + elif shape_variation == 'elongated': |
| 57 | + elongation_axis = random.choice(['x', 'y', 'z']) |
| 58 | + if elongation_axis == 'x': |
| 59 | + particle.scale = (random.uniform(0.015, 0.02), |
| 60 | + random.uniform(0.008, 0.012), |
| 61 | + random.uniform(0.008, 0.012)) |
| 62 | + elif elongation_axis == 'y': |
| 63 | + particle.scale = (random.uniform(0.008, 0.012), |
| 64 | + random.uniform(0.015, 0.02), |
| 65 | + random.uniform(0.008, 0.012)) |
| 66 | + elif elongation_axis == 'z': |
| 67 | + particle.scale = (random.uniform(0.008, 0.012), |
| 68 | + random.uniform(0.008, 0.012), |
| 69 | + random.uniform(0.015, 0.02)) |
| 70 | + else: |
| 71 | + particle.scale = (random.uniform(0.008, 0.012), |
| 72 | + random.uniform(0.008, 0.012), |
| 73 | + random.uniform(0.008, 0.012)) |
| 74 | + |
| 75 | + # Create a new material for lunar regolith appearance |
| 76 | + mat = bpy.data.materials.new(name="LunarRegolithMaterial") |
| 77 | + mat.use_nodes = True |
| 78 | + nodes = mat.node_tree.nodes |
| 79 | + links = mat.node_tree.links |
| 80 | + |
| 81 | + # Clear default nodes |
| 82 | + for node in nodes: |
| 83 | + nodes.remove(node) |
| 84 | + |
| 85 | + # Add necessary nodes |
| 86 | + output_node = nodes.new(type='ShaderNodeOutputMaterial') |
| 87 | + principled_bsdf = nodes.new(type='ShaderNodeBsdfPrincipled') |
| 88 | + |
| 89 | + # Set base color to dark gray (lunar regolith color) |
| 90 | + principled_bsdf.inputs['Base Color'].default_value = (0.3, 0.3, 0.3, 1.0) |
| 91 | + |
| 92 | + # Increase roughness for a dusty look |
| 93 | + principled_bsdf.inputs['Roughness'].default_value = 0.9 |
| 94 | + |
| 95 | + # Add noise texture for bump mapping (surface roughness) |
| 96 | + noise_texture = nodes.new(type='ShaderNodeTexNoise') |
| 97 | + noise_texture.inputs['Scale'].default_value = 30.0 |
| 98 | + noise_texture.inputs['Detail'].default_value = 2.0 |
| 99 | + |
| 100 | + # Add bump node to connect noise texture for surface detail |
| 101 | + bump_node = nodes.new(type='ShaderNodeBump') |
| 102 | + bump_node.inputs['Strength'].default_value = 0.3 # Moderate bump effect |
| 103 | + |
| 104 | + # Connect noise texture to bump node |
| 105 | + links.new(noise_texture.outputs['Fac'], bump_node.inputs['Height']) |
| 106 | + |
| 107 | + # Connect bump node to Principled BSDF normal input |
| 108 | + links.new(bump_node.outputs['Normal'], principled_bsdf.inputs['Normal']) |
| 109 | + |
| 110 | + # Connect BSDF to Material Output |
| 111 | + links.new(principled_bsdf.outputs['BSDF'], output_node.inputs['Surface']) |
| 112 | + |
| 113 | + # Assign the material to the particle |
| 114 | + particle.data.materials.append(mat) |
| 115 | + |
| 116 | + return particle, mat |
| 117 | + |
| 118 | +def export_particle_as_obj(particle, file_name): |
| 119 | + # Select the particle before exporting |
| 120 | + bpy.ops.object.select_all(action='DESELECT') # Deselect all |
| 121 | + particle.select_set(True) # Select the particle to export |
| 122 | + |
| 123 | + # Export the particle as an OBJ file with the material |
| 124 | + bpy.ops.wm.obj_export(filepath=file_name, export_selected_objects=True, export_materials=True) |
| 125 | + |
| 126 | +def delete_particle_and_material(particle, material): |
| 127 | + # Delete the particle and its material |
| 128 | + bpy.data.objects.remove(particle, do_unlink=True) |
| 129 | + bpy.data.materials.remove(material, do_unlink=True) |
| 130 | + |
| 131 | +# Number of granular particles to generate and export |
| 132 | +num_particles = 10 |
| 133 | + |
| 134 | +for i in range(num_particles): |
| 135 | + # Step 1: Generate a particle |
| 136 | + particle, material = create_granular_particle() |
| 137 | + |
| 138 | + # Step 2: Export the particle as OBJ with the material |
| 139 | + obj_file_name = os.path.join(export_directory, f"particle_{i+1}.obj") |
| 140 | + export_particle_as_obj(particle, obj_file_name) |
| 141 | + |
| 142 | + # Step 3: Delete the particle and material |
| 143 | + delete_particle_and_material(particle, material) |
| 144 | + |
| 145 | + print(f"Particle {i+1} exported and deleted.") |
| 146 | + |
0 commit comments