forestで使ったBlender Pythonのコード

これで使ったやつです。

円柱での分割


import bpy


# コレクションの作成(インターネットから拾った)
# https://bluebirdofoz.hatenablog.com/entry/2019/12/26/222941
# (https://docs.blender.org/api/current/bpy.ops.collection.html)
def make_object_collection(arg_collectionname="Default") -> bool:

    # もし同名のコレクションが既に存在していれば新規作成しない
    if arg_collectionname in bpy.data.collections:
        return False
    # コレクションを作成する
    newCollection = bpy.data.collections.new(arg_collectionname)

    # コレクションをシーンに追加する
    bpy.context.scene.collection.children.link(newCollection)
    return newCollection


# 差分ブーリアン
def difbooleanFnc(obj01, obj02):
    bool01 = obj01.modifiers.new("BOOL", type="BOOLEAN")
    bool01.object = obj02
    bool01.operation = "DIFFERENCE"
    bpy.ops.object.modifier_apply({"object": obj01}, modifier="BOOL")


# 交差ブーリアン
def intbooleanFnc(obj01, obj02):
    bool01 = obj01.modifiers.new("BOOL", type="BOOLEAN")
    bool01.object = obj02
    bool01.operation = "INTERSECT"
    bpy.ops.object.modifier_apply({"object": obj01}, modifier="BOOL")


n = 10  # 分割数
resolution = 32  # 基準図形の頂点数
scalexyz = (0.1, 0.1, 10)  # 基準図形の最初の大きさ
coll_name = "test1"  # コレクション名

make_object_collection(arg_collectionname=coll_name)
target = bpy.context.object

# 分割の基準となる基本図形(円柱)の追加
bpy.ops.mesh.primitive_cylinder_add(
    enter_editmode=False,
    align="WORLD",
    location=(0, 0, 0),
    scale=scalexyz,
    vertices=resolution,
)
# 基準オブジェクト = 基本図形(円柱)
cylinder = bpy.context.object

# 差分ブーリアンと交差ブーリアンを繰り返す
for i in range(0, n):
    # 分割用に分割対象のコピーを作成
    cp = target.copy()
    cp.data = cp.data.copy()
   # バックエンドにあったコピーをコレクションに入れる
    bpy.data.collections[coll_name].objects.link(cp)
    if i != 1:
        # 差分ブーリアン適用
        difbooleanFnc(cp, cylinder)
    cylinder.scale = (i + 1, i + 1, 1)
    if i != n - 1:
        # 交差ブーリアン適用
        intbooleanFnc(cp, cylinder)

# バックエンドに存在している不要なオブジェクトを削除(やらなくてもよい)
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
target.hide_render = True
target.hide_set(True)
# 基準オブジェクトの削除
bpy.data.objects.remove(cylinder)


スクリプトの説明?動画です。以前学校の宿題として出したやつ。


使い方としては
⓪Scriptingの欄で「+新規作成」ボタンを押す
①Scripitingのウィンドウにコードをコピペする
②分割する対象をマウスでクリックして選択しておく
③上の▷ボタンで実行

でいけます。アドオン化してないので、原始的(?)な方法です。
なんかおかしい場合は円柱のサイズがおかしい可能性があります。稚拙なスクリプトなのでそこは手動で調整しましょう。とりあえずShift+Aから出せるようなプリミティブな図形のサイズをいい感じに切れるようにしています。

Blender Pythonのことは正直よくわかってないけど使用例を改変して何とかしてる感じです。

また、他の人が作ったスクリプト(https://blender.stackexchange.com/questions/133086/slicing-an-object-in-4-parts/133136#133136 )を改変した等分割のスクリプトも使いました。基本の仕組みは同じなのですが、その人がすごいため、ややこしいことやっててわけわかりません。多分変な位置、角度でおいててもいい感じに分割してくれるようにしてくれてるのかなぁという感じです。segments_x、segments_y、segments_zが分割数です。


等分割(断面マテリアル設定+collection版)

「#」で無効にしているところの#を消して有効にして、「Material.001」という名前のマテリアルを用意したら断面のマテリアルを設定できます。スクリプト内の「Material.001」の名前を変えれば他のマテリアルでもいけます。あとCollection Nameは毎回手動で変えてください!
あと今知ったのですが、1オブジェクトだけコレクションに入ってないので手動で移してください。直せそうだったら直しますが無理そうだったら放置します。

import bpy
from mathutils import Matrix, Vector

# コレクションの作成
# https://bluebirdofoz.hatenablog.com/entry/2019/12/26/222941
# (https://docs.blender.org/api/current/bpy.ops.collection.html)
def make_object_collection(arg_collectionname="Default") -> bool:
    """コレクションの作成
    
    コレクションの作成
    (https://docs.blender.org/api/current/bpy.ops.collection.html)

    Keyword Arguments:
        arg_collectionname {str} -- 作成コレクション名 (default: {"Default"})
    
    Returns:
        bool -- 作成の正否
    """
    
    # もし同名のコレクションが既に存在していれば新規作成しない
    if arg_collectionname in bpy.data.collections:
        return False
    # コレクションを作成する
    newCollection = bpy.data.collections.new(arg_collectionname)

    # コレクションをシーンに追加する
    bpy.context.scene.collection.children.link(newCollection)
    return newCollection


context = bpy.context
ob = context.object
size = 4 * max(ob.dimensions)
mw = ob.matrix_world

def bbox(ob):
    return (Vector(b) for b in ob.bound_box)

def bbox_center(ob):
    return sum(bbox(ob), Vector()) / 8
    
def bbox_axes(ob):
    bb = list(bbox(ob))
    return tuple(bb[i] for i in (0, 4, 3, 1))

o, x, y, z = bbox_axes(ob)        

bpy.ops.mesh.primitive_plane_add(
        location=mw @ bbox_center(ob),
        size=size)
chopper = context.object
# chopper_mat = bpy.data.materials["Material.001"] 
# chopper.data.materials.append(chopper_mat)
m = chopper.modifiers.new("Sol", type='SOLIDIFY')
m.thickness = size


chopper.select_set(False)

def chop(ob, start, end, segments):
    slices = []
    planes = [(f, start.lerp(end, f / segments)) 
            for f in   range(1, segments)]

    for i, p in planes:
        m.thickness = -size
        bm = ob.modifiers.new("BOOL",type="BOOLEAN")
        
        bm.object = chopper
        bm.operation = 'DIFFERENCE'
        M = (mw @ end - mw @ start).to_track_quat('Z', 'X').to_matrix().to_4x4()
        M.translation = mw @ p

        chopper.matrix_world = M
        cp = ob.copy()
        cp.data = cp.data.copy()
        
        
        bpy.data.collections[coll_name].objects.link(cp)
        bpy.ops.object.modifier_apply({"object" : cp}, modifier="BOOL")
        slices.append(cp)
        m.thickness = size
        bpy.ops.object.modifier_apply(
                {"object" : ob}, modifier = 'BOOL')
    slices.append(ob)
    return slices

coll_name = "test" # 毎回変えてね
segments_x = 4
segments_y = 1
segments_z = 3

make_object_collection(arg_collectionname=coll_name)
for ox in chop(ob, o, x, segments_x):
    for oy in chop(ox, o, y, segments_y):
        chop(oy, o, z, segments_z)
      
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=False, do_recursive=True)
bpy.data.objects.remove(chopper)


正直Blender Pythonじゃないとできないことって、他ソフトとの連携だったり効率化のためのアドオン化(UIをつける)、ライブラリを使える(もともとBlender用とかではない他の人が作ったコードを使う)…だったりするので、やっぱり即効性があるのはGeometry Nodesです実は。こっちの方が可逆性があるし、Scriptingのめんどくさい調べ作業よりかはだいぶ覚える項目が少ないので、おすすめかもです。

今調べてたのですが、ジオメトリーノードオタクたちが同じようなことをやっているので、まあ、そういうことです。ただ今のところいわゆるfor構文がないし、可逆的にやろうとすると激重になりそうなので、ちゃんと同じのはないかも?


この記事が気に入ったらサポートをしてみませんか?