はじめに
3Dプリンターで色々作って遊んでます。造形するデータは3DCGソフトのBlenderを使っています。
Blenderで3Dデータを作る人は珍しくないと思いますが、私のようにスクリプトで3Dデータを作る人は少ないかもと思い、その方法を紹介することにしました。
(私が3Dプリンターで作るものは留め具や固定台だったりとカクカクしたものが多いです。そーいうものを色々作っていくうちに、独特なスクリプトになりました。)
スクリプトを使う利点とは
私が3Dデータの作成にプログラムを使う理由は便利だから。
例えば、大量の穴が開いた板を作るときにプログラムのforループを使うことができます。
穴の大きさや位置を変えたい場合は、プログラムのパラメータを変更し再実行するだけです。
これはスクリプト実行時の様子です。プログラムを実行するだけで穴あきの板ができます。プログラムさえ書いてしまえば、実行するだけでデータが完成します(楽!)
余談ですが、この穴あき板はレゴと互換性のあるラズベリーパイの固定板として作ったものです。
Blenderでスクリプトを使う方法
Blenderを起動し、メニュー一覧にあるScripting
をクリックするとスクリプト用のウィンドウが開きます。+New
ボタンをクリックするとスクリプトを記載するウィンドウが開きます。
スクリプトを記載するウィンドウに今回紹介したスクリプトなどを張り付けて実行すると3Dデータが作成されます。
スクリプトについて
ここからスクリプトの紹介です。
ソースはこちら
スクリプトのソースはここにあります。順次公開していってます。
スクリプトは改善を繰り返しており、ファイル名の数値が大きいほど新しい改善が進んでいます。ここでは、現時点の最新である07_MDFボードの留め具.txt
のスクリプトをベースに解説します。
スクリプトの構成
このスクリプトは独特な書き方になっているので、まずは構成を紹介します。
このスクリプトは大きく3つの区分にわかれます。区分(1)と(3)は基本的に変更しません。作りたいオブジェクトの情報をは区分(2)に記載していきます。
区分 | 区分の説明 |
---|---|
区分(1) | 関数群。さわらない |
区分(2) | 3DCGデータ作成。 オブジェクトの作成/移動/複製/回転/くり抜き など設定 |
区分(3) | 関数creObjFlow ,editFlow を実行。さわらない |
スクリプトの使い方
スクリプトの基本的な使い方を紹介します。
(1)オブジェクトを作成する
例えば直方体を作る場合はこのようにします。ここでは直方体を作成せず変数olist
に作成したいオブジェクトの情報を登録だけしています。
ScaleX = 30.0
ScaleY = 30.0
ScaleZ = 5.0
LocateX = -11.0
LocateY = -10.0
LocateZ = -17.0
olist.append( {"OBJ": "CUBE", "name": "Bottom01", "location": (LocateX,LocateY,LocateZ), "size": (ScaleX,ScaleY,ScaleZ), "rotation": (0.0,0.0,0.0), "material": mlist[40],} )
実際にオブジェクトを作成するのは区分(3)のcreObjFlow
関数です。
## オブジェクト作成
creObjFlow(olist)
円柱を作成する場合はこのようになります。1.5708
とは90度回転しています。
radiusTumaTate = 2.4
radiusTumaYoko = 2.4
depthTuma = 6.0
LocateZ = -11.5
olist.append( {"OBJ": "CYLINDER","name": "TOTU-01","location": (-1, -18.5, 0),"radius": radiusTumaYoko,"depth": depthTuma,"rotation": (0,1.5708,0),"material": mlist[30],} )
このスクリプトで作れるオブジェクトの種類は以下です。
直方体
olist.append( {"OBJ": "CUBE", "name": "SIDE01", "location": ( 18.5, 0, -2.5), "size": (8, 4, 17.9), "rotation": (0,0,0), "material": mlist[12],} )
円柱
olist.append( {"OBJ": "CYLINDER","name": "HOLE01", "location": (18.1, 0, 0),"radius": 1.6,"depth": 6.0,"rotation": (1.5708,0,0),"material": mlist[89],} )
円錐
olist.append( {"OBJ": "CONE","name": "CONE01","location": (0,0,0),"vertices": 32, "radius1": 20, "radius2": 0, "depth": 30,"rotation": (0,0,0),"material": mlist[1],} )
olist.append( {"OBJ": "CONE","name": "CONE01","location": (40,0,0),"vertices": 32, "radius1": 10, "radius2": 5, "depth": 20,"rotation": (0,0,0),"material": mlist[2],} )
円錐はこのようになります。radius1
は底面の半径で、radius2
は上面の半径です。
(2)オブジェクトを他のオブジェクトでくり抜く
2つのオブジェクトを作成し、くり抜きをする例を紹介します。
まずは2つオブジェクトを作成します。オブジェクトのname
が重要なのでスクリプト無いで一意の値を設定します。例では、Guide01
という直方体とTOTU-95
という円柱の2つのオブジェクトを作成します。
# 板に穴をあけるガイド
ScaleX = 30.0
ScaleY = 30.0
ScaleZ = 4.0
LocateX = -20.0
LocateY = -19.0
LocateZ = -28.0
olist.append( {"OBJ": "CUBE", "name": "Guide01", "location": (LocateX,LocateY,LocateZ), "size": (ScaleX,ScaleY,ScaleZ), "rotation": (0.0,0.0,0.0), "material": mlist[50],} )
# 四隅の支柱に穴をあける
radiusTumaTate = 1.45
radiusTumaYoko = 1.45
depthTuma = 140.0
LocateZ = -8
olist.append( {"OBJ": "CYLINDER","name": "TOTU-95","location": (-18.5, -17.5, LocateZ),"radius": radiusTumaYoko,"depth": depthTuma,"rotation": (0,0,0),"material": mlist[34],} )
直方体Guide01
を、円柱TOTU-95
でくり抜く場合は以下のようにします。
alist.append({"function": "booleanFnc", "args": ["Guide01","TOTU-95"]})
くり抜きのために作った円柱TOTU-95
は不要になったので削除します。
alist.append({"function": "deleteFnc", "args": ["TOTU-95"]})
これらの処理を実際に行うのは区分(3)のeditFlow
関数です。
## オブジェクト加工
editFlow(alist)
このスクリプトで作成されるデータはこのようになります。
(3)オブジェクトを移動する
作成したオブジェクトを移動します。
以下はSIDE01
という名前のオブジェクトを座標(18, 0, 0)に移動します。
alist.append({"function": "moveFnc2", "args": ["SIDE01", 18, 0, 0]})
(2)で紹介したくり抜きと、この移動を繰り返すと無数の穴が開いた板を作る場合に便利です。
(4)オブジェクトを回転する
オブジェクトを回転します。
以下はKADO-B-01
という名前のオブジェクトをY軸を基準に 60度 回転します。
alist.append({"function": "rotateFnc", "args": ["KADO-B-01",0,60,0]})
(5)オブジェクトを複製する
オブジェクトを複製します。
以下はFUTA-M-01
という名前のオブジェクト複製し、FUTA-M-11
という名前のオブジェクトを新規に作成します。また、作成したオブジェクトはFUTA-M-01
の位置からX軸方向に、-50 移動したところに配置します。
alist.append({"function": "copyFnc", "args": ["FUTA-M-01", "FUTA-M-11",-50, 0, 0]})
これは、板に無数の突起を作りたいときに便利です。
(6)オブジェクトの角を丸める
オブジェクトの角を丸めます。
以下はCUBE02
という名前のオブジェクトの角を丸めます。
# 角を丸めない
olist.append( {"OBJ": "CUBE", "name": "CUBE01", "location": (30,0,1), "size": (40, 50, 60), "rotation": (0,0,0), "material": mlist[20],} )
# 角を丸める
olist.append( {"OBJ": "CUBE", "name": "CUBE02", "location": (-30,0,1), "size": (40, 50, 60), "rotation": (0,0,0), "material": mlist[10],} )
alist.append({"function": "bevelFnc", "args": ["CUBE02", 15, 0.1]})
スクリプトの["CUBE02", 15, 0.1]
について、15は丸めるときに面の解像度で数値を大きくするほど滑らかな丸みになります。0.1 は、丸める幅で数値を大きくすると丸める幅が大きくなります。
例を実行するとこのようになります。黄色が丸みなしです。
(7)オブジェクトを削除する
既に紹介していますが、オブジェクトを削除します。
以下はTOTU-95
という名前のオブジェクトを削除します。
alist.append({"function": "deleteFnc", "args": ["TOTU-95"]})
くり抜き用に作ったオブジェクトなど不要になったものを削除するのに便利です。
(8)オブジェクトの面を押し出す
オブジェクトの指定した面を押し出します。
以下はCUBE02
という名前のオブジェクトの1つの面を押し出します。
# 面を押し出さない
olist.append( {"OBJ": "CUBE", "name": "CUBE01", "location": (30,0,1), "size": (40, 50, 60), "rotation": (0,0,0), "material": mlist[20],} )
# 面を押し出す
olist.append( {"OBJ": "CUBE", "name": "CUBE02", "location": (-30,0,1), "size": (40, 50, 60), "rotation": (0,0,0), "material": mlist[10],} )
alist.append({"function": "moveMesh", "args": ["CUBE02", 1, 10,10,10]})
スクリプトの["CUBE02", 1, 10,10,10]
について、1 はCUBE02
の押し出す面の番号です。CUBE02
は直方体なので、面は 0 ~ 5 の番号が割り振られています。10,10,10
は、押し出す面の移動先の座標(x,y,z)です。
スクリプトを実行するとこのようになります。
(9)オブジェクトの面を拡大する
オブジェクトの指定した面を押し出します。
以下はCUBE02
という名前のオブジェクトの1つの面を拡大します。
olist.append( {"OBJ": "CUBE", "name": "CUBE02", "location": (-30,0,1), "size": (40, 50, 60), "rotation": (0,0,0), "material": mlist[23],} )
alist.append({"function": "scaleMesh", "args": ["CUBE02", 5, 2.5,2.5,2.5]})
スクリプトを実行するとこのようになります。
(10)オブジェクトをグループ化する
複数のオブジェクトをグループ化します。(オブジェクトのJoinです)
以下は、A-CONE02
をA-CONE01
にJoinします。
関数の引数"args": ["A-CONE01","A-CONE02"]
の0番目A-CONE01
がJoin元になります。
olist.append( {"OBJ": "CONE","name": "A-CONE01","location": (0,0,0),"vertices": 20, "radius1": 23, "radius2": 21, "depth": 10,"rotation": (0,0,0),"material": mlist[22],} )
olist.append( {"OBJ": "CONE","name": "A-CONE02","location": (0,0,0),"vertices": 30, "radius1": 16, "radius2": 19, "depth": 12,"rotation": (0,0,0),"material": mlist[21],} )
alist.append({"function": "addJoin", "args": ["A-CONE01","A-CONE02"]})
複数のオブジェクトをまとめて移動やコピーしたいときに便利です。
(11)オブジェクト名を変更する
オブジェクト名を変更します。
alist.append({"function": "renameFnc", "args": ["{変更前}","{変更後}"]})
オブジェクト名A-CONE01
をFUGA
に変更する場合はこのようになります。
alist.append({"function": "renameFnc", "args": ["A-CONE01","FUGA"]})
(12)オブジェクトの中心を指定した座標に移動する
指定したオブジェクトの中心座標を指定した座標に移動します。
以下の場合、オブジェクト名HAKO
の中心座標を(0,0,100)
に移動します。
alist.append({"function": "moveOriginFnc", "args": ["HAKO",0,0,100]})
(13)オブジェクトの中心座標を修正する
指定したオブジェクトの中心座標を更新します。
以下の場合、オブジェクト名HAKO
の中心座標を(0,0,30)
に更新します。
alist.append({"function": "resetOriginFnc", "args": ["HAKO-2x2",0,0,30]})
利用例
スクリプトを組合せて使う例を紹介します。
(1)円錐と押し出し
円錐と押し出しでビンのような形を作ります。
ネジ穴の作成に使えそうです。
# ただの円錐
olist.append( {"OBJ": "CONE","name": "CONE01","location": (0,-40,0),"vertices": 32, "radius1": 10, "radius2": 5, "depth": 20,"rotation": (0,0,0),"material": mlist[9],} )
# 押し出しと組合せる円錐
olist.append( {"OBJ": "CONE","name": "CONE02","location": (0,0,0),"vertices": 32, "radius1": 10, "radius2": 5, "depth": 20,"rotation": (0,0,0),"material": mlist[3],} )
alist.append({"function": "moveMesh", "args": ["CONE02", 30, 0,0,20]})
alist.append({"function": "moveMesh", "args": ["CONE02", 33, 0,0,-30]})
(2)無数の穴を空ける
移動と、くり抜きをforループで繰り返し穴を空けます。
この繰り返し処理を使えるのがスクリプトで3Dデータを作るメリットと思います。
# 穴を空ける板
olist.append( {"OBJ": "CUBE", "name": "CUBE01", "location": (0,0,0), "size": (40, 50, 10), "rotation": (0,0,0), "material": mlist[20],} )
# 空ける穴
olist.append( {"OBJ": "CYLINDER","name": "HOLE01", "location": (0, 20, 0),"radius": 3,"depth": 15.0,"rotation": (0,0,0),"material": mlist[89],} )
# 穴を空けるときの基準の位置
iniX=15
iniY=15
iniZ=0
# 穴を空ける位置
curX=0
curY=0
curZ=0
for i in range(3):
for j in range(3):
curX=iniX - 8*i # X座標を設定
curY=iniY - 8*j # Y座標を設定
# 穴を移動する
alist.append({"function": "moveFnc2", "args": ["HOLE01", curX, curY, curZ]})
# 穴を空ける
alist.append({"function": "booleanFnc", "args": ["CUBE01","HOLE01"]})
# 不要になったオブジェクトを削除
alist.append({"function": "deleteFnc", "args": ["HOLE01"]})
ループの回数を変更すれば好きなだけ穴空きにできます。
(3)加工したオブジェクトでくり抜く
利用例の(1)と(2)を組み合わせた例です。
# くり抜きする板
olist.append( {"OBJ": "CUBE", "name": "CUBE01", "location": (0,0,0), "size": (200, 125, 20), "rotation": (0,0,0), "material": mlist[20],} )
# くり抜く型
olist.append( {"OBJ": "CONE","name": "HOLE01","location": (0,0,0),"vertices": 32, "radius1": 10, "radius2": 5, "depth": 20,"rotation": (0,0,0),"material": mlist[3],} )
alist.append({"function": "moveMesh", "args": ["HOLE01", 30, 0,0,20]})
alist.append({"function": "moveMesh", "args": ["HOLE01", 33, 0,0,-30]})
iniX=15
iniY=15
iniZ=0
curX=0
curY=0
curZ=0
for i in range(5):
for j in range(5):
curX=iniX - 20*i
curY=iniY - 20*j
if i % 2 == 0:
# オブジェクト HOLE01 をY軸に対し180度回転
alist.append({"function": "rotateFnc", "args": ["HOLE01",0,180,0]})
else:
alist.append({"function": "rotateFnc", "args": ["HOLE01",0,0,0]})
# オブジェクト HOLE01 を移動
alist.append({"function": "moveFnc2", "args": ["HOLE01", curX, curY, curZ]})
# オブジェクト CUBE01 を HOLE01 でくり抜き
alist.append({"function": "booleanFnc", "args": ["CUBE01","HOLE01"]})
# オブジェクト HOLE01 を削除
alist.append({"function": "deleteFnc", "args": ["HOLE01"]})
Blender楽しい
Blenderのスクリプト活用により、より複雑なデザインやオブジェクトを簡単に作成できるようになりました。今後もさらなる改善や新機能の追加を目指していきます :)