Skip to content

Instantly share code, notes, and snippets.

@squarednob
Last active December 11, 2017 09:59
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save squarednob/790e47b30d73f769384fc858318726ca to your computer and use it in GitHub Desktop.

パーティクルシステムがついたオブジェクトを選択し、scripting画面でコードをコピペして、runで実行できます。

bacterial_particle_jp.py:

増殖パーティクルのpythonスニペット。以下の機能を追加することで、疑似的な増殖粒子を作ります。

  • pythonによる制御なしでは、birth timeとlifetimeの間で、各粒子がインデックスの順に均一な間隔で誕生します。これをpythonスクリプトによって、一定間隔で今の倍の粒子が誕生するようにbirth_timeを粒子ごとに割り振ります。
  • さらに、新たに誕生した粒子を既存の粒子の近くに配置することで、粒子の増殖と近似させます。

particle_control_jp.py:

パーティクルをpythonでコントロールする方法の説明です。

  • いったんパーティクルシミュレーションを再生すると、pythonで粒子位置などを変えても、キャッシュされた位置に戻されて反映されません。キャッシュをクリアするために、seed値などのパーティクルシステムの値をいったん変更して、すぐに元の値に戻します。
  • foreach_set()やforeach_get()で一挙にパーティクルのパーティクルの位置を操作する場合、代入する配列は、一次元配列となります。[x0,y0,z0,x1,y1,z1,...]のように、粒子ごとのxyz値は並置します。
import bpy
import numpy as np
import random
# 簡単にアクセスするための変数代入。
obj = bpy.context.object
par_system = obj.particle_systems[0]
particles = par_system.particles
par_setting = particles.data.settings
par_len = par_setting.count
par_frame_start = par_setting.frame_start
par_frame_end = par_setting.frame_end
# パーティクルシステムのプリセット設定。
def preset():
par_setting.lifetime = 500
par_setting.normal_factor = 0
par_setting.effector_weights.gravity = 0
par_setting.physics_type = 'FLUID'
# physics_typeの変更を反映するために、フレーム番号を変える。
bpy.context.scene.frame_set(1)
par_setting.fluid.stiffness = 0.03
par_setting.fluid.fluid_radius = 2
par_setting.fluid.spring_force = 0.25
# 分裂を再現するために、誕生した粒子を既存の粒子のインデックスに対応させる。
def cellDivision(reloc_index,par_birth,birth_start,frame_step,initial_particles,growth_rate,division_sequential):
# 最初に誕生した粒子のフレームと位置インデックス配列。
prev_index = initial_particles
par_birth[:prev_index] = birth_start
reloc_index = np.append(reloc_index, np.arange(0,initial_particles,1))
count = 1
while prev_index < par_len:
next_index = prev_index*growth_rate
# 誕生フレームにランダム性追加。
newborn = len(par_birth[prev_index:next_index])
rand = np.random.randint(0,frame_step, size=newborn)
par_birth[prev_index:next_index] = (frame_step*count) - (rand*division_sequential)
# 新しく生まれた粒子を、既存の粒子のインデックスに対応させる。
reloc_index = np.append(reloc_index, np.arange(0, prev_index, 1))
prev_index = next_index
count +=1
reloc_index = reloc_index.astype(int)
return (reloc_index, par_birth)
# フレームごとに粒子の位置をセット。
def setFrameLocation(reloc_index,par_birth,par_loc,
growth_mode,loc_range,initial_particles,x_axis,y_axis,z_axis):
frame_start = par_frame_start
if growth_mode == 'cell':
frame_end = bpy.context.scene.frame_end
else:
frame_end = par_frame_end
# 最初のbirth_timeは以下で自動的にリセットされる。
for f in range(int(frame_start), int(frame_end)):
bpy.context.scene.frame_set(f)
par_index = np.where(par_birth == f)
if len(par_index[0]) > 0:
for i in par_index[0]:
# birth_timeをセット。
particles[i].birth_time = par_birth[i]
# 新しい位置をセット。
par_i = 0
if growth_mode == 'cell':
par_i = reloc_index[i]
elif growth_mode == 'proceed':
par_i = random.randint(0,initial_particles)
else:
par_i = random.randint(0,i)
new_loc = particles[par_i].location
# 誕生位置にランダム性追加。
rand_fac = 1
rand_floor = 0.001
rand_axis = np.random.random(3) * [x_axis, y_axis, z_axis]
rand_loc = np.random.uniform(-1,1,3) * (loc_range + rand_floor)
new_loc[0] += rand_loc[0] * rand_axis[0] * rand_fac
new_loc[1] += rand_loc[1] * rand_axis[1] * rand_fac
new_loc[2] += rand_loc[2] * rand_axis[2] * rand_fac
particles[i].location = new_loc
par_loc[i*3:(i*3)+3] = new_loc
return par_loc
def execute(growth_mode,birth_delay, birth_start,loc_range,x_axis,y_axis,z_axis,
initial_particles,frame_step,growth_rate,division_sequential):
# いったんパーティクルシミュレーションを再生すると、粒子位置が記憶されてしまうので、リセットするためにシードを変えてから戻す。
par_system.seed += 1
par_system.seed -= 1
# 最初のbirth_time配列。
par_birth = np.array([0.0]*par_len, dtype='float')
particles.foreach_get('birth_time', par_birth)
# 最初の位置配列。
par_loc = np.array([0,0,0]*par_len, dtype='float')
particles.foreach_get('location', par_loc)
# このインデックスの粒子近くに、新しく誕生した粒子を配置する。
reloc_index = np.array([])
if growth_mode == 'cell':
reloc_index, par_birth = cellDivision(reloc_index,par_birth,
birth_start,frame_step,initial_particles,growth_rate,division_sequential)
par_birth = par_birth.astype(int)
par_loc = setFrameLocation(reloc_index,par_birth,par_loc,
growth_mode,loc_range,initial_particles,x_axis,y_axis,z_axis)
# 誕生の遅延。
par_birth += birth_delay
# 再び位置と誕生時間をセット。
if growth_mode == 'cell':
particles.foreach_set('location', par_loc)
particles.foreach_set('birth_time', par_birth)
bpy.context.scene.frame_current = 1
# 一般的なパラメータ。
growth_mode = 'cell' # 増殖モード。cell=周期的増殖、proceed=方向性をもって進む。random=ランダムな増殖。
birth_delay = 5
birth_start = 1
loc_range = 0.1
x_axis = 0.1
y_axis = 0.1
z_axis = 0
# cellとproceedモードのパラメータ。
initial_particles = 3
# cellモードのパラメータ。
frame_step = 20
growth_rate = 2
division_sequential = 0
# 実行。
preset()
execute(growth_mode,birth_delay,birth_start,loc_range,x_axis,y_axis,z_axis,
initial_particles,frame_step,growth_rate,division_sequential)
import bpy
import numpy as np
# 選択したオブジェクトのパーティクルシステムを参照。
obj = bpy.context.object
par_system = obj.particle_systems[0]
particles = par_system.particles
# パーティクルの数、フレーム、位置を変数に格納。
frame = 20
par_len = len(particles)
# パーティクルの位置はフラットな一次元配列であることに注意。[x0.y0,z0,x1,y1,z1,...]のように並べる。
par_loc = np.array([0,0,0]*par_len, dtype='float')
# いったんパーティクルシミュレーションをプレイすると、その時のパーティクルの位置が残ってしまって、foreach_set()で位置を変えても反映されない。
# seedなどをいったん変えることで、シミュレーション後のパーティクル位置情報をリセット。
par_system.seed += 1
par_system.seed -= 1
for i in range(frame):
bpy.context.scene.frame_set(i)
# 現在の全パーティクルの位置をフラットな一次元配列として取得。
particles.foreach_get("location", par_loc)
# 全パーティクルのx,y,z位置を、0.2だけ増加させる。
par_loc += 0.2
# パーティクルの位置を更新。
particles.foreach_set("location", par_loc)
print(par_loc)
bpy.context.scene.frame_current = 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment