Blenderのスクリプトで悪魔のような絵面を生み出す

**※Blenderのバージョンは2.80です**

　Blenderにおけるある種のモデリングは、Pythonスクリプトを用いてその時間を壊滅的に短縮することができます。大量の物体を並べるような作業は、その最たるものです。実例を示していきます。

#単純な図形を並べる

　プリミティブ（単純な図形）を並べていきます。

　ウィンドウを分割して、片方にカーソルを当てた状態で`Shift+F11`を押すと以下のように。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d0555859-a88c-6aac-8142-2380170fae11.png)

「新規」を押して新しいスクリプトを作っていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/47b86905-ad2b-c783-7162-04629a8287ae.png)

　以下の文面をコピペします。

```python

import bpy

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

max = 20

for i in range(0,max):
    for j in range(0,max):
        bpy.ops.mesh.primitive_cone_add(
        radius1=.7,
        location=(i,j,0)
        )
```

「スクリプト実行」をクリックするか、`Alt+P`を入力すると、以下のような絵面が誕生します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/fa5aa62c-141e-cf47-b9e9-4a9dcd042f90.png)

　`max`の値をいじると繰り返しの幅を指定できます。`20`ぐらいだったら大体のパソコンで大丈夫だと思いますが、`100`だと数分固まるかもしれません。`radius1`は底面の半径を指定しており、`0.7`に指定しているのは$\frac{1}{\sqrt{2}}$に近い値にしてお互いの底面がぎりぎり接するようにするためです。

##よりドラマチックに

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/a21d2667-f0b5-c484-7d02-5d8bf26cb8dd.png)

```python

import bpy
import math

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

max = 20

for i in range(0,max):
    for j in range(0,max):
        height = 2+math.sin(i+j)
        bpy.ops.mesh.primitive_cone_add(
        radius1=.7,
        location=(i,j,height/2),
        depth = height)

```

　円錐の高さにバリエーションを加えます。三角関数を使って高さを周期的に変化させます。`depth`で高さを指定した分だけ、その半分をz位置に加算しないと底の高さが合わないので`depth = height`、`location=(i,j,height/2)`としています。

##高さも使う

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/1664c05a-4ec2-9796-17ca-fb113a5dfd53.png)


```python
import bpy

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

max = 5

for i in range(-max,max):
    for j in range(-max,max):
        for k in range(-max,max):
            bpy.ops.mesh.primitive_cone_add(
            location=(i*4,j*4,k*4)
            )
```

　宇宙を感じる。今後の計算の簡便のため、イテレーションの範囲を`0`＜`max`から`-max`＜`max`としています。

##変化も加える（ランダム）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/e960e7e4-8348-8506-f480-236fb059a954.png)

```python

import bpy
import numpy

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

max = 5

for i in range(-max,max):
    for j in range(-max,max):
        for k in range(-max,max):
            bpy.ops.mesh.primitive_cone_add(
            location=(i*4,j*4,k*4),
            rotation=numpy.random.rand(3)*3,
            )
```
　`rotation`に乱数を指定しています。これはこれで美しい。

##変化も加える（規則的）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d60cb427-faea-1c9c-9755-d4bd70a0c2f4.png)


```Python
import bpy
import math

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

max = 5

for i in range(-max,max):
    for j in range(-max,max):
        for k in range(-max,max):
            dist = math.sqrt(i*i+j*j)
            zx = math.atan2(j,i)
            zy = -math.pi/2-math.atan2(k,dist)
            bpy.ops.mesh.primitive_cone_add(
            location=(i*8,j*8,k*8),
            depth=dist*2,
            rotation=(0,zy,zx)
            )
```

　すべての円錐が中心を向くようにしました。
　$\tan^{-1}(\frac{j}{i})$で経度、$\tan^{-1}(\frac{k}{\sqrt{i^2+j^2}})$で緯度を算出しています。中心に向かっている感を演出するために`depth`も調整しています。

#任意の図形をコピーして並べる

　プリミティブだけだとつまらないので、任意の物体をコピーして並べていきます。コピー元となる一体を配置＆選択しておいて、それをひたすらコピーしていくという手法でやっていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/aaaa1690-9d61-2970-3418-f030b733bee2.png)

　これが、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/46eed908-5aa4-dc89-c230-f299a4b4c4dc.png)

　このような感じに。

```Python
import bpy
import numpy

for i in range(0,500):
    bpy.ops.object.duplicate()
    bpy.context.object.location = numpy.random.rand(3)*40-20
    bpy.context.object.rotation_euler = numpy.random.rand(3)*3
```

#大量の図形をインポートして並べる

　さらに図形に変化を加えたい場合、あらかじめfbxなりobjなりplyを用意しておいて、それを大量にインポートするという手法が考えられます。ちょうど[この前](https://qiita.com/hibit/items/f78b8a167cc75ce749dc)大量の人体モデルを生成したことですし、一括インポートして並べてみましょう。特定のフォルダにあらかじめ大量のファイルを用意しておいて、通し番号なりで一括して処理できるように下準備をする必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/0fe0f649-3a31-7a8a-3d7d-ef32a88fa648.png)

　ネオエクスデスかな？

```Python
import bpy
import numpy

#ここにファイルのパス（通し番号の直前まで）を記入
dir = r'～～～' 

for i in range(0,500):
    #通し番号と拡張子を指定（このコードだとobj）
    path = dir + str(i) + '.obj' 
    bpy.ops.import_scene.obj(filepath=path)

for i in range(0,500):
    bpy.data.objects[i].location = numpy.random.rand(3)*20-10
```