Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER"> Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER"> Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER">
import bpy
import math
import mathutils
bl_info = {
"name": "HoleMaker",
"author": "AI",
"version": (1, 2),
"blender": (4, 0, 0),
"location": "View3D > Sidebar(N) > HoleMaker",
"description": "Generates a hollowed sphere with uniformly distributed holes.",
"category": "Object",
}
class HoleMakerProperties(bpy.types.PropertyGroup):
sphere_radius: bpy.props.FloatProperty(
name="球体の半径",
default=1.0,
min=0.1
)
thickness: bpy.props.FloatProperty(
name="球体の厚み (空洞化)",
description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します",
default=0.01,
min=0.001
)
cylinder_radius: bpy.props.FloatProperty(
name="円柱の半径",
default=0.1,
min=0.01
)
cylinder_count: bpy.props.IntProperty(
name="円柱の本数",
default=12,
min=1
)
class OBJECT_OT_hole_maker(bpy.types.Operator):
bl_idname = "object.hole_maker"
bl_label = "穴あき中空球体を生成"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props = context.scene.hole_maker_props
# 1. 選択状態をクリア
bpy.ops.object.select_all(action='DESELECT')
# 2. ベースとなる球体(外側)を作成
bpy.ops.mesh.primitive_uv_sphere_add(
radius=props.sphere_radius,
location=(0, 0, 0)
)
sphere = context.active_object
# 3. 内部空洞化の処理
inner_radius = props.sphere_radius - props.thickness
if inner_radius > 0:
# くり抜き用の内側の球体を作成
bpy.ops.mesh.primitive_uv_sphere_add(
radius=inner_radius,
location=(0, 0, 0)
)
inner_sphere = context.active_object
# ブーリアンを適用するために外側の球体をアクティブにする
context.view_layer.objects.active = sphere
# 空洞化用ブーリアンモディファイアの設定
mod_hollow = sphere.modifiers.new(name="HollowBoolean", type='BOOLEAN')
mod_hollow.operation = 'DIFFERENCE'
try:
mod_hollow.solver = 'FAST'
except TypeError:
mod_hollow.solver = 'FLOAT'
mod_hollow.object = inner_sphere
# モディファイアを適用
bpy.ops.object.modifier_apply(modifier=mod_hollow.name)
# くり抜きに使った内側の球体を削除
bpy.data.objects.remove(inner_sphere, do_unlink=True)
# 4. 円柱の生成と貫通穴のブーリアン処理
n = props.cylinder_count
phi = math.pi * (3.0 - math.sqrt(5.0)) # フィボナッチ黄金角
cylinder_depth = props.sphere_radius * 2.5
for i in range(n):
y = 1.0 - (i / float(n - 1)) * 2.0 if n > 1 else 0.0
radius = math.sqrt(1.0 - y * y)
theta = phi * i
x = math.cos(theta) * radius
z = math.sin(theta) * radius
vec = mathutils.Vector((x, y, z))
rot = vec.to_track_quat('Z', 'Y').to_euler()
bpy.ops.mesh.primitive_cylinder_add(
radius=props.cylinder_radius,
depth=cylinder_depth,
location=(0, 0, 0),
rotation=rot
)
cylinder = context.active_object
context.view_layer.objects.active = sphere
mod = sphere.modifiers.new(name="HoleBoolean", type='BOOLEAN')
mod.operation = 'DIFFERENCE'
try:
mod.solver = 'FAST'
except TypeError:
mod.solver = 'FLOAT'
mod.object = cylinder
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(cylinder, do_unlink=True)
# 処理完了後、生成した球体を選択・アクティブ状態にする
sphere.select_set(True)
context.view_layer.objects.active = sphere
return {'FINISHED'}
class VIEW3D_PT_hole_maker(bpy.types.Panel):
bl_label = "HoleMaker"
bl_idname = "VIEW3D_PT_hole_maker"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'HoleMaker'
def draw(self, context):
layout = self.layout
props = context.scene.hole_maker_props
# UIプロパティの配置
col = layout.column(align=True)
col.prop(props, "sphere_radius")
col.prop(props, "thickness") # 厚み(0.01)を調整できるパラメーター
col.prop(props, "cylinder_radius")
col.prop(props, "cylinder_count")
layout.separator()
# 実行ボタンの配置
layout.operator("object.hole_maker", text="穴あき中空球体を生成", icon='MESH_UVSPHERE')
classes = (
HoleMakerProperties,
OBJECT_OT_hole_maker,
VIEW3D_PT_hole_maker,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.hole_maker_props = bpy.props.PointerProperty(type=HoleMakerProperties)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.hole_maker_props
if __name__ == "__main__":
register()