Книга: Введение в написание скриптов на Питоне для Блендера 2.5x. Примеры кода
Симуляции
Разделы на этой странице:
Симуляции
В этом разделе мы обращаемся к потенциалу симуляций Блендера из Питона. Некоторые из примеров были вдохновлены книгой Bounce, Tumble and Splash Тони Муллена (ищите в Сети великолепный перевод от Morthan'а, пользуясь случаем, передаю ему большое СПАСИБО! - прим. пер.). Однако, большинство рендеров не выглядят так же хорошо, как в книге Муллена, так как целью этих заметок не было найти оптимальный способ для настройки параметров, а скорее чтобы показать, как их можно настраивать из Питона.
Частицы
Эта программа добавляет две системы частиц.
#---------------------------------------------------
# File particle.py
#---------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector, Matrix
from math import pi
def run(origo):
# Добавление меша эмиттера
origin = Vector(origo)
bpy.ops.mesh.primitive_plane_add(location=origin)
emitter = bpy.context.object
# --- Система частиц 1: Падение и сдувание капель ---
# Добавление первой системы частиц
bpy.ops.object.particle_system_add()
psys1 = emitter.particle_systems[-1]
psys1.name = 'Drops'
# Эмиссия, испускание
pset1 = psys1.settings
pset1.name = 'DropSettings'
pset1.frame_start = 40
pset1.frame_end = 200
pset1.lifetime = 50
pset1.lifetime_random = 0.4
pset1.emit_from = 'FACE'
pset1.use_render_emitter = True
pset1.object_align_factor = (0,0,1)
# Физика
pset1.physics_type = 'NEWTON'
pset1.mass = 2.5
pset1.particle_size = 0.3
pset1.use_multiply_size_mass = True
# Веса эффекторов
ew = pset1.effector_weights
ew.gravity = 1.0
ew.wind = 1.0
# Дочерние частицы
pset1.child_nbr = 10
pset1.rendered_child_count = 10
pset1.child_type = 'SIMPLE'
# Отображение и рендер
pset1.draw_percentage = 100
pset1.draw_method = 'CROSS'
pset1.material = 1
pset1.particle_size = 0.1
pset1.render_type = 'HALO'
pset1.render_step = 3
# ------------ Эффектор ветра -----
# Добавление эффектора ветра
bpy.ops.object.effector_add(
type='WIND',
enter_editmode=False,
location = origin - Vector((0,3,0)),
rotation = (-pi/2, 0, 0))
wind = bpy.context.object
# Настройки полей
fld = wind.field
fld.strength = 2.3
fld.noise = 3.2
fld.flow = 0.3
# --- Система частиц 2: Обезьяны на ветру ----
# Добавление обезьяны, используемой как объект размножения
# Скрытие обезьяны в слое 2
layers = 20*[False]
layers[1] = True
bpy.ops.mesh.primitive_monkey_add(
location=origin+Vector((0,5,0)),
rotation = (pi/2, 0, 0),
layers = layers)
monkey = bpy.context.object
#Добавление второй системы частиц
bpy.context.scene.objects.active = emitter
bpy.ops.object.particle_system_add()
psys2 = emitter.particle_systems[-1]
psys2.name = 'Monkeys'
pset2 = psys2.settings
pset2.name = 'MonkeySettings'
# Эмиссия, испускание
pset2.count = 4
pset2.frame_start = 1
pset2.frame_end = 50
pset2.lifetime = 250
pset2.emit_from = 'FACE'
pset2.use_render_emitter = True
# Скорость
pset2.factor_random = 0.5
# Физика
pset2.physics_type = 'NEWTON'
pset2.brownian_factor = 0.5
# Веса эффекторов
ew = pset2.effector_weights
ew.gravity = 0
ew.wind = 0.2
# Дочерние частицы
pset2.child_nbr = 1
pset2.rendered_child_count = 1
pset2.child_size = 3
pset2.child_type = 'SIMPLE'
# Отображение и рендер
pset2.draw_percentage = 1
pset2.draw_method = 'RENDER'
pset2.dupli_object = monkey
pset2.material = 1
pset2.particle_size = 0.1
pset2.render_type = 'OBJECT'
pset2.render_step = 3
return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run((0,0,0))
bpy.ops.screen.animation_play(reverse=False, sync=False)
Волосы
Эта программа добавляет сферу с волосами. Для волос строится шейдер типа strand.
#---------------------------------------------------
# File hair.py
#---------------------------------------------------
import bpy
def createHead(origin):
# Добавление меша эмиттера
bpy.ops.mesh.primitive_ico_sphere_add(location=origin)
ob = bpy.context.object
bpy.ops.object.shade_smooth()
# Создание группы вершин scalp (скальп), а также добавление вершин и весов
scalp = ob.vertex_groups.new('Scalp')
for v in ob.data.vertices:
z = v.co[2]
y = v.co[1]
if z > 0.3 or y > 0.3:
w = 2*(z-0.3)
if w > 1:
w = 1
scalp.add([v.index], w, 'REPLACE')
return ob
def createMaterials(ob):
# Некоторый материал для кожи
skinmat = bpy.data.materials.new('Skin')
skinmat.diffuse_color = (0.6,0.3,0)
# Материал strand для волос
hairmat = bpy.data.materials.new('Strand')
hairmat.diffuse_color = (0.2,0.04,0.0)
hairmat.specular_intensity = 0
# Прозрачность
hairmat.use_transparency = True
hairmat.transparency_method = 'Z_TRANSPARENCY'
hairmat.alpha = 0
# Strand. Нужно включить use Blender units перед заданием размеров.
strand = hairmat.strand
strand.use_blender_units = True
strand.root_size = 0.01
strand.tip_size = 0.0025
strand.size_min = 0.001
#strand.use_surface_diffuse = True # read-only
strand.use_tangent_shading = True
# Текстура
tex = bpy.data.textures.new('Blend', type = 'BLEND')
tex.progression = 'LINEAR'
tex.use_flip_axis = 'HORIZONTAL'
# Создание цветовой полосы для цвета и альфа-канала
tex.use_color_ramp = True
tex.color_ramp.interpolation = 'B_SPLINE'
# Точки на цветовой полосе: (pos, rgba)
# Не знаю, как добавлять точки на полосу
rampTable = [
(0.0, (0.23,0.07,0.03,0.75)),
#(0.2, (0.4,0.4,0,0.5)),
#(0.7, (0.6,0.6,0,0.5)),
(1.0, (0.4,0.3,0.05,0))
]
elts = tex.color_ramp.elements
n = 0
for (pos, rgba) in rampTable:
elts[n].position = pos
elts[n].color = rgba
n += 1
# Добавление текстуры blend к hairmat
mtex = hairmat.texture_slots.add()
mtex.texture = tex
mtex.texture_coords = 'STRAND'
mtex.use_map_color_diffuse = True
mtex.use_map_alpha = True
# Добавление материала к мешу
ob.data.materials.append(skinmat) # Material 1 = Skin
ob.data.materials.append(hairmat) # Material 2 = Strand
return
def createHair(ob):
# Создание системы частиц hair
bpy.ops.object.particle_system_add()
psys = ob.particle_systems.active
psys.name = 'Hair'
# psys.global_hair = True
psys.vertex_group_density = 'Scalp'
pset = psys.settings
pset.type = 'HAIR'
pset.name = 'HairSettings'
# Эмиссия
pset.count = 40
pset.hair_step = 7
pset.emit_from = 'FACE'
# Рендер
pset.material = 2
pset.use_render_emitter = True
pset.render_type = 'PATH'
pset.use_strand_primitive = True
pset.use_hair_bspline = True
# Дочерние частицы
pset.child_type = 'SIMPLE'
pset.child_nbr = 10
pset.rendered_child_count = 500
pset.child_length = 1.0
pset.child_length_threshold = 0.0
pset.child_roundness = 0.4
pset.clump_factor = 0.862
pset.clump_shape = 0.999
pset.roughness_endpoint = 0.0
pset.roughness_end_shape = 1.0
pset.roughness_1 = 0.0
pset.roughness_1_size = 1.0
pset.roughness_2 = 0.0
pset.roughness_2_size = 1.0
pset.roughness_2_threshold = 0.0
pset.kink = 'CURL'
pset.kink_amplitude = 0.2
pset.kink_shape = 0.0
pset.kink_frequency = 2.0
return
def run(origin):
ob = createHead(origin)
createMaterials(ob)
createHair(ob) return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run((0,0,0))
Редактируемые волосы
Эта программа добавляет сферу с редактируемыми волосами от полученных направляющих (guides) волос. Если мы переключаемся в режим редактирования, все пряди становятся прямыми, то есть результат редактирования теряется. Это можно предотвратить, если вы переключитесь в режим частиц, выберите объект, и переключитесь обратно в режим объекта. К сожалению, я не нашел способа сделать это с помощью скрипта.
#---------------------------------------------------
# File edit_hair.py
# Имеет недостатки, но может быть интересна в любом случае.
#---------------------------------------------------
import bpy
def createHead():
# Добавление меша эмиттера
bpy.ops.mesh.primitive_ico_sphere_add()
ob = bpy.context.object
ob.name = 'EditedHair'
bpy.ops.object.shade_smooth()
return ob
def createHair(ob, guides):
nGuides = len(guides)
nSteps = len(guides[0])
# Создание системы частиц hair
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.particle_system_add()
psys = ob.particle_systems.active
psys.name = 'Hair'
# Настройки частиц
pset = psys.settings
pset.type = 'HAIR'
pset.name = 'HairSettings'
pset.count = nGuides
pset.hair_step = nSteps-1
pset.emit_from = 'FACE'
pset.use_render_emitter = True
# Дочерние частицы
pset.child_type = 'SIMPLE'
pset.child_nbr = 6
pset.rendered_child_count = 300
pset.child_length = 1.0
pset.child_length_threshold = 0.0
# Отсоединение волос и переключение в режим редактирования частиц
bpy.ops.particle.disconnect_hair(all=True)
bpy.ops.particle.particle_edit_toggle()
# Настройка всех ключевых волос
dt = 100.0/(nSteps-1)
dw = 1.0/(nSteps-1)
for m in range(nGuides):
guide = guides[m]
part = psys.particles[m]
part.location = guide[0]
for n in range(1, nSteps):
point = guide[n]
h = part.hair_keys[n-1]
h.co_hair_space = point
h.time = n*dt
h.weight = 1.0 - n*dw
# Переключение режима редактирования частиц
bpy.ops.particle.select_all(action='SELECT')
bpy.ops.particle.particle_edit_toggle()
# Подсоединение волос к мешу
# Во время рендера случится Segmentation violation, если эта строка отсутствует.
bpy.ops.particle.connect_hair(all=True)
# К сожалению, здесь шаг действий вручную представляется необходимым:
# 1. Переключиться в режим частиц
# 2. Прикоснуться к объекту с кистью
# 3. Переключиться в режим объектов
# 4. Переключиться в режим редактирования
# 5. Переключиться в режим объектов
# Это должно соответствовать коду ниже, но терпит неудачу из-за
# неверного контекста
'''
bpy.ops.particle.particle_edit_toggle()
bpy.ops.particle.brush_edit()
bpy.ops.particle.particle_edit_toggle()
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
'''
return
# Направляющие волос. Четыре волоса с пятью точками.
hairGuides = [
[(-0.334596,0.863821,0.368362),
(-0.351643,2.33203,-0.24479),
(0.0811583,2.76695,-0.758137),
(0.244019,2.73683,-1.5408),
(0.199297,2.60424,-2.32847)],
[(0.646501,0.361173,0.662151),
(1.33538,-0.15509,1.17099),
(2.07275,0.296789,0.668891),
(2.55172,0.767097,-0.0723231),
(2.75942,1.5089,-0.709962)],
[(-0.892345,-0.0182112,0.438324),
(-1.5723,0.484807,0.971839),
(-2.2393,0.116525,0.324168),
(-2.18426,-0.00867975,-0.666435),
(-1.99681,-0.0600535,-1.64737)],
[(-0.0154996,0.0387489,0.995887),
(-0.205679,-0.528201,1.79738),
(-0.191354,0.36126,2.25417),
(0.0876127,1.1781,1.74925),
(0.300626,1.48545,0.821801)] ]
def run(origin):
ob = createHead()
createHair(ob, hairGuides)
ob.location = origin
return
if __name__ == "__main__":
run((0,0,0))
Ткань
Эта программа добавляет плоскость с модификатором ткани. У плоскости есть родитель — обруч, который движется вниз, где она встречается с препятствием-сферой. Влияние модификатора ткани находится под контролем группы вершин, а это значит, что углы движутся с обручем, в то время как середина деформируется препятствием. Плоскости присваивается материал со стресс-отображением прозрачности.
#----------------------------------------------------------
# File cloth.py
#----------------------------------------------------------
import bpy, mathutils, math from mathutils import Vector
def run(origin):
side = 4
diagonal = side/math.sqrt(2)
hoopRad = 0.1
eps = 0.75
nDivs = 40
scn = bpy.context.scene
# Добавление сферы, выступающей в качестве объекта столкновения
bpy.ops.mesh.primitive_ico_sphere_add(location=origin)
sphere = bpy.context.object
bpy.ops.object.shade_smooth()
# Добавление модификатора collision к сфере
bpy.ops.object.modifier_add(type='COLLISION')
cset = sphere.modifiers[0].settings
cset.thickness_outer = 0.2
cset.thickness_inner = 0.5
cset.permeability = 0.2
cset.stickness = 0.2
bpy.ops.object.modifier_add(type='SUBSURF')
# Добавление кольца
center = origin+Vector((0,0,2))
bpy.ops.mesh.primitive_torus_add(
major_radius= diagonal + hoopRad,
minor_radius= hoopRad,
location=center,
rotation=(0, 0, 0))
bpy.ops.object.shade_smooth()
ring = bpy.context.object
# Добавление плоскости над сферой и привязка её к кольцу
bpy.ops.mesh.primitive_plane_add(location=(0,0,0))
bpy.ops.transform.resize(value=(side/2,side/2,1))
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.subdivide(number_cuts=nDivs)
bpy.ops.object.mode_set(mode='OBJECT')
plane = bpy.context.object
plane.parent = ring
me = plane.data
# Создание группы вершин. Объект не должен быть активным?
scn.objects.active = None
grp = plane.vertex_groups.new('Group')
for v in plane.data.vertices:
r = v.co - center
x = r.length/diagonal
w = 3*(x-eps)/(1-eps)
if w > 1:
w = 1
if w > 0:
grp.add([v.index], w, 'REPLACE')
# Активация плоскости снова
scn.objects.active = plane
# Добавление модификатора cloth (ткань)
cloth = plane.modifiers.new(name='Cloth', type='CLOTH')
cset = cloth.settings
cset.use_pin_cloth = True
cset.vertex_group_mass = 'Group'
# Настройки шёлка, скопировано из "scripts/presets/cloth/silk.py"
cset.quality = 5
cset.mass = 0.150
cset.structural_stiffness = 5
cset.bending_stiffness = 0.05
cset.spring_damping = 0
cset.air_damping = 1
# Сглаженное затенение
plane.select = True
bpy.ops.object.shade_smooth()
bpy.ops.object.modifier_add(type='SUBSURF')
# Текстура Blend
tex = bpy.data.textures.new('Blend', type = 'BLEND')
tex.progression = 'SPHERICAL'
tex.intensity = 1.0
tex.contrast = 1.0
tex.use_color_ramp = True
elts = tex.color_ramp.elements
elts[0].color = (0, 0, 0, 1)
elts[0].position = 0.56
elts[1].color = (1, 1, 1, 0)
elts[1].position = 0.63
# материал Rubber (Резиновый)
mat = bpy.data.materials.new('Rubber')
mat.diffuse_color = (1,0,0)
mat.use_transparency = True
mat.alpha = 0.25
mtex = mat.texture_slots.add()
mtex.texture = tex
mtex.texture_coords = 'STRESS'
mtex.use_map_color_diffuse = True
mtex.diffuse_color_factor = 0.25
mtex.use_map_alpha = True
mtex.alpha_factor = 1.0
mtex.blend_type = 'ADD'
# Добавление материала к плоскости
plane.data.materials.append(mat)
# Анимация кольца
ring.location = center
ring.keyframe_insert('location', index=2, frame=1)
ring.location = origin - Vector((0,0,0.5))
ring.keyframe_insert('location', index=2, frame=20)
ring.location = center
return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run(Vector((0,0,0)))
scn = bpy.context.scene
scn.frame_current = 1
bpy.ops.screen.animation_play()
Мягкие тела
Эта программа добавляет конус с модификатором softbody (мягкое тело) и плоскость-препятствие.
#----------------------------------------------------------
)
# File softbody.py
#----------------------------------------------------------
import bpy
import mathutils
from mathutils import Vector
def run(origin):
# Добавление материала
red = bpy.data.materials.new('Red')
red.diffuse_color = (1,0,0)
blue = bpy.data.materials.new('Blue')
blue.diffuse_color = (0,0,1)
# Добавление конуса
bpy.ops.mesh.primitive_cone_add(
vertices=4,
radius=1.5,
cap_end=True)
ob1 = bpy.context.object
me1 = ob1.data
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.subdivide(number_cuts=5, smoothness=1, fractal=1)
bpy.ops.object.mode_set(mode='OBJECT')
# Странно, нужен новый меш, который является копией
verts = []
faces = []
for v in me1.vertices:
verts.append(v.co)
for f in me1.faces:
faces.append(f.vertices)
me2 = bpy.data.meshes.new('Drop')
me2.from_pydata(verts, [], faces)
me2.update(calc_edges=True)
# Установка гладкости граням (smooth)
for f in me2.faces: f.use_smooth = True
# Добавление нового объекта и его активация
ob2 = bpy.data.objects.new('Drop', me2)
scn = bpy.context.scene
scn.objects.link(ob2)
scn.objects.unlink(ob1)
scn.objects.active = ob2
# Добавление групп вершин
top = ob2.vertex_groups.new('Top')
bottom = ob2.vertex_groups.new('Bottom')
for v in me2.vertices:
w = v.co[2] - 0.2
if w < 0:
if w < -1:
w = -1
bottom.add([v.index], -w, 'REPLACE'
elif w > 0:
if w > 1:
w = 1
top.add([v.index], w, 'REPLACE')
bpy.ops.object.mode_set(mode='OBJECT')
ob2.location = origin
me2.materials.append(blue)
# Добавление модификатора softbody
mod = ob2.modifiers.new(name='SoftBody', type='SOFT_BODY')
sbset = mod.settings
# Мягкое тело
sbset.friction = 0.6
sbset.speed = 0.4
sbset.mass = 8.1
# Цель
sbset.goal_default = 0.7
sbset.goal_spring = 0.3
sbset.goal_friction = 0.0
sbset.vertex_group_goal = 'Top'
# Края мягкого тела
sbset.pull = 0.6
sbset.push = 0.1
sbset.bend = 0.1
sbset.aerodynamics_type = 'LIFT_FORCE'
sbset.aero = 0.5
# Добавление вихря
bpy.ops.object.effector_add(
type='VORTEX',
location=origin+Vector((0,0,-4)))
vortex = bpy.context.object
fset = vortex.field
fset.strength = 4.5
fset.shape = 'PLANE'
fset.apply_to_location = False
fset.apply_to_rotation = True
fset.falloff_type = 'TUBE'
# Добавление плоскости столкновения
# Предупреждение. Столкновение объектов делает симуляцию очень медленной!
bpy.ops.mesh.primitive_plane_add(
location=origin-Vector((0,0,1.7)))
bpy.ops.transform.resize(value=(4, 4, 4))
plane = bpy.context.object
plane.data.materials.append(red)
mod = plane.modifiers.new(name='Collision', type='COLLISION')
return
if __name__ == "__main__":
bpy.context.scene.frame_end = 600
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
run(Vector((0,0,6)))
bpy.ops.screen.animation_play()
#bpy.ops.render.opengl(animation=True)
Ткань, мягкие тела и текстуры displace
Эта программа показывает три различных метода размахивания флагом: модификатором ткани, модификатором мягких тел, и с помощью анимированных текстур смещения.
#----------------------------------------------------------
# File flags.py
# Создает флаг из мягкого тела и флаг из ткани на ветру.
# Update to API rev. 36816
#----------------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector
from math import pi
# Размер флага, глобальные переменные
xmax = 40
zmax = 24
ds = 2.0/xmax
def makeFlag(name, origin, invert):
# Добавление нового меша, который будет флагом
me = bpy.data.meshes.new(name)
flag = bpy.data.objects.new(name, me)
scn = bpy.context.scene
scn.objects.link(flag)
scn.objects.active = flag
# Построение меша флага
verts = []
faces = []
for x in range(xmax):
for z in range(zmax):
verts.append(((x+0.5)*ds, 0, z*ds))
if x > 0 and z > 0:
faces.append(((x-1)*zmax+(z-1), (x-1)*zmax+z, x*zmax+z, x*zmax+(z-1)))
me.from_pydata(verts, [], faces)
me.update(calc_edges=True)
flag.location = origin
# Добавление групп вершин
grp = flag.vertex_groups.new('Pole')
for v in me.vertices:
w = 1.5 - 7*v.co[0]
if invert:
if w > 1:
grp.add([v.index], 0.0, 'REPLACE')
else:
grp.add([v.index], 1-w, 'REPLACE')
else:
if w > 1:
grp.add([v.index], 1.0, 'REPLACE')
elif w > 0:
grp.add([v.index], w, 'REPLACE')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.shade_smooth()
return flag
def makePole(origin):
bpy.ops.mesh.primitive_cylinder_add(
vertices=32,
radius=ds/2,
depth=1,
cap_ends=True)
bpy.ops.transform.resize(value=(1, 1, 2.5))
pole = bpy.context.object
pole.location = origin
return pole
def addSoftBodyModifier(ob):
mod = ob.modifiers.new(name='SoftBody', type='SOFT_BODY')
sbset = mod.settings
# Мягкое тело
sbset.friction = 0.3
sbset.speed = 1.4
sbset.mass = 0.9
# Цель
sbset.goal_default = 0.3
sbset.goal_spring = 0.1
sbset.goal_friction = 0.1
sbset.vertex_group_goal = 'Pole'
# Рёбра мягкого тела
sbset.pull = 0.1
sbset.push = 0.1
sbset.bend = 0.1
sbset.aerodynamics_type = 'LIFT_FORCE'
sbset.aero = 0.5
#Веса эффектора
ew = sbset.effector_weights
ew.gravity = 0.1
ew.wind = 0.8
return
def addClothModifier(ob):
cloth = ob.modifiers.new(name='Cloth', type='CLOTH')
cset = cloth.settings
cset.quality = 4
cset.mass = 0.2
cset.structural_stiffness = 0.5
cset.bending_stiffness = 0.05
cset.spring_damping = 0
cset.air_damping = 0.3
cset.use_pin_cloth = True
cset.vertex_group_mass = 'Pole'
#Веса эффектора
ew = cset.effector_weights
ew.gravity = 0.1
ew.wind = 1.0 return
def addWindEffector(origin):
# Добавление эффектора ветра
bpy.ops.object.effector_add(
type='WIND',
location=origin,
rotation=(pi/2,0,0))
wind = bpy.context.object
fset = wind.field
fset.strength = -2.0
fset.noise = 10.0
fset.flow = 0.8
fset.shape = 'PLANE'
return
def addFlagMaterial(name, ob, color1, color2):
# Текстура флага
tex = bpy.data.textures.new('Flag', type = 'WOOD')
tex.noise_basis_2 = 'TRI'
tex.wood_type = 'RINGS'
# Создание материала
mat = bpy.data.materials.new(name)
mat.diffuse_color = color1
# Добавление текстурного слота для текстуры цвета
mtex = mat.texture_slots.add()
mtex.texture = tex
mtex.texture_coords = 'ORCO'
mtex.use_map_color_diffuse = True
mtex.color = color2
# Добавление материала к флагу
ob.data.materials.append(mat)
return mat
def createDisplacementTexture(mat):
tex = bpy.data.textures.new('Flag', type = 'WOOD')
tex.noise_basis_2 = 'SIN'
tex.wood_type = 'BANDNOISE'
tex.noise_type = 'SOFT_NOISE'
tex.noise_scale = 0.576
tex.turbulence = 9.0
# Сохранение текстуры в материале для легкого доступа. Не необходимо на самом деле.
mtex = mat.texture_slots.add()
mtex.texture = tex
mat.use_textures[1] = False
return tex
def addDisplacementModifier(ob, tex, vgrp, empty):
mod = ob.modifiers.new('Displace', 'DISPLACE')
mod.texture = tex
mod.vertex_group = vgrp
mod.direction = 'NORMAL'
mod.texture_coords = 'OBJECT'
mod.texture_coords_object = empty
mod.mid_level = 0.0
mod.strength = 0.1
print("'%s' '%s'" % (vgrp, mod.vertex_group))
mod.vertex_group = vgrp
print("'%s' '%s'" % (vgrp, mod.vertex_group))
return mod
def createAndAnimateEmpty(origin):
bpy.ops.object.add(type='EMPTY', location=origin)
empty = bpy.context.object
scn = bpy.context.scene
scn.frame_current = 1
bpy.ops.anim.keyframe_insert_menu(type='Location')
scn.frame_current = 26
bpy.ops.transform.translate(value=(1,0,1))
bpy.ops.anim.keyframe_insert_menu(type='Location')
scn.frame_current = 1
for fcu in empty.animation_data.action.fcurves:
fcu.extrapolation = 'LINEAR'
for kp in fcu.keyframe_points:
kp.interpolation = 'LINEAR'
return empty
def run(origin):
# Создание флагов и полей
flag1 = makeFlag('SoftBodyFlag', origin+Vector((-3,0,0)), False)
flag2 = makeFlag('ClothFlag', origin+Vector((0,0,0)), False)
flag3 = makeFlag('DisplacementFlag', origin+Vector((3,0,0)), True)
pole1 = makePole(origin+Vector((-3,0,0)))
pole2 = makePole(origin+Vector((0,0,0)))
pole3 = makePole(origin+Vector((3,0,0)))
# Материалы
mat1 = addFlagMaterial('SoftBodyFlag', flag1, (1,0,0), (0,0,1))
mat2 = addFlagMaterial('ClothFlag', flag2, (0,1,0), (1,1,0))
mat3 = addFlagMaterial('DisplacementFlag', flag3, (1,0,1), (0,1,0))
white = bpy.data.materials.new('White')
white.diffuse_color = (1,1,1)
pole1.data.materials.append(white)
pole2.data.materials.append(white)
pole3.data.materials.append(white)
# Добавление модификаторов и ветра
addSoftBodyModifier(flag1)
addClothModifier(flag2)
addWindEffector(origin+Vector((-1,-2,0)))
# Создание смещения
tex3 = createDisplacementTexture(mat3)
empty = createAndAnimateEmpty(origin + Vector((3,0,0)))
mod = addDisplacementModifier(flag3, tex3, 'POLE', empty)
return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run(Vector((0,0,0)))
bpy.ops.screen.animation_play()
Частицы и модификатор Explode (взрыв)
Пуля с невидимой системой частиц стреляет в хрустальный шар. Шар разрушается, и части падают на пол.
Эффект достигается за счет придания шару модификатора взрыва, который запускается системой частиц. Идея заключалась в том, чтобы сделать это в системе частиц reactor
, которая вызывается системой частиц пули. Тем не менее, частицы reactor
, по-видимому, еще не включены в Blender 2.5, так что частицы шара устанавливаются на выброс в определенное время, а не по реакции.
#----------------------------------------------------------
)
# File crystal.py
#----------------------------------------------------------
import bpy, mathutils, math
from mathutils import *
def addSphere(name, size, origin):
bpy.ops.mesh.primitive_ico_sphere_add(
subdivisions=2,
size=size,
location=origin)
bpy.ops.object.shade_smooth()
bpy.ops.object.modifier_add(type='SUBSURF')
ob = bpy.context.object
ob.name = name
return ob
def addFloor(name, origin, hidden):
bpy.ops.mesh.primitive_plane_add(location=origin)
bpy.ops.transform.resize(value=(30, 30, 30))
floor = bpy.context.object
floor.name = name
if hidden:
floor.hide = True
floor.hide_render = True
return floor
# Матариал пола
voronoi = bpy.data.textures.new('Voronoi', type = 'VORONOI')
voronoi.color_mode = 'POSITION'
voronoi.noise_scale = 0.1
plastic = bpy.data.materials.new('Plastic')
plastic.diffuse_color = (1,1,0)
plastic.diffuse_intensity = 0.1
mtex = plastic.texture_slots.add()
mtex.texture = voronoi
mtex.texture_coords = 'ORCO'
mtex.color = (0,0,1)
floor.data.materials.append(plastic)
return floor
def run(origin):
# ----------- Материалы
red = bpy.data.materials.new('Red')
red.diffuse_color = (1,0,0)
red.specular_hardness = 200
rmir = red.raytrace_mirror
rmir.use = True
rmir.distance = 0.001
rmir.fade_to = 'FADE_TO_MATERIAL'
rmir.distance = 0.0
rmir.reflect_factor = 0.7
rmir.gloss_factor = 0.4
grey = bpy.data.materials.new('Grey'
grey.diffuse_color = (0.5,0.5,0.5)
# ----------- Пуля — маленькая сфера
bullet = addSphere('Bullet', 0.2, origin)
bullet.data.materials.append(grey)
# Анимация пули
scn = bpy.context.scene
scn.frame_current = 51
bullet.location = origin
bpy.ops.anim.keyframe_insert_menu(type='Location')
bullet.location = origin+Vector((0,30,0))
scn.frame_current = 251
bpy.ops.anim.keyframe_insert_menu(type='Location')
scn.frame_current = 1
action = bullet.animation_data.action
for fcu in action.fcurves:
fcu.extrapolation = 'LINEAR'
for kp in fcu.keyframe_points:
kp.interpolation = 'LINEAR'
# Система частиц Trail (след) для пули
bpy.ops.object.particle_system_add()
trail = bullet.particle_systems[0]
trail.name = 'Trail'
fset = trail.settings
# Эмиссия
fset.name = 'TrailSettings'
fset.count = 1000 fset.frame_start = 1
fset.frame_end = 250
fset.lifetime = 25
fset.emit_from = 'FACE'
fset.use_render_emitter = True
# Скорость
fset.normal_factor = 1.0
fset.factor_random = 0.5
# Физика
fset.physics_type = 'NEWTON'
fset.mass = 0
# Установка всех эффекторных весов в ноль
ew = fset.effector_weights
ew.gravity = 0.0
# Не рендерить
fset.draw_method = 'DOT'
fset.render_type = 'NONE'
# -------------- Шар
ball = addSphere('Ball', 1.0, origin)
ball.data.materials.append(red)
# Система частиц
bpy.ops.object.particle_system_add()
react = ball.particle_systems[0]
react.name = 'React'
fset = react.settings
# Эмиссия
fset.name = 'TrailSettings'
fset.count = 50
fset.frame_start = 47
fset.frame_end = 57
fset.lifetime = 250
fset.emit_from = 'FACE'
fset.use_render_emitter = True
# Скорость
fset.normal_factor = 5.0
fset.factor_random = 2.5
# Физика
fset.physics_type = 'NEWTON'
fset.mass = 1.0
# Не рендерить
fset.draw_method = 'CROSS'
fset.render_type = 'NONE'
# Модификатор Explode
mod = ball.modifiers.new(name='Explode', type='EXPLODE')
mod.use_edge_cut = True
mod.show_unborn = True
mod.show_alive = True
mod.show_dead = True
mod.use_size = False
# ---- Скрытый пол с модификатором collision (столкновения)
hidden = addFloor('Hidden', origin+Vector((0,0,-3.9)), True)
mod = hidden.modifiers.new(name='Collision', type='COLLISION')
mset = mod.settings
mset.permeability = 0.01
mset.stickness = 0.1
mset.use_particle_kill = False
mset.damping_factor = 0.6
mset.damping_random = 0.2
mset.friction_factor = 0.3
mset.friction_random = 0.1
addFloor('Floor', Vector((0,0,-4)), False)
return
if __name__ == "__main__":
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Камера, освещение
bpy.ops.object.camera_add(
location = Vector((12,-12,4)),
rotation = Vector((70,0,45))*math.pi/180)
cam = bpy.context.object.data
cam.lens = 35
bpy.ops.object.lamp_add(type='POINT',
location = Vector((11,-7,6)))
bpy.ops.object.lamp_add(type='POINT',
location =Vector((-7,-10,2)))
run(Vector((0,0,0)))
Частицы огня и дыма
Эта программа добавляет две системы частиц для огня и дыма. Частицы отображаются в виде билбордов с процедурными текстурами.
#---------------------------------------------------
)
# File fire.py
#---------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector, Matrix
from math import pi
def createEmitter(origin):
bpy.ops.mesh.primitive_plane_add(location=origin)
emitter = bpy.context.object
bpy.ops.mesh.uv_texture_add()
return emitter
def createFire(emitter):
# Добавление первой системы частиц — огня
bpy.context.scene.objects.active = emitter
bpy.ops.object.particle_system_add()
fire = emitter.particle_systems[-1]
fire.name = 'Fire'
fset = fire.settings
# Эмиссия
fset.name = 'FireSettings'
fset.count = 100
fset.frame_start = 1
fset.frame_end = 200
fset.lifetime = 70
fset.lifetime_random = 0.2
fset.emit_from = 'FACE'
fset.use_render_emitter = False
fset.distribution = 'RAND'
fset.object_align_factor = (0,0,1)
# Скорость
fset.normal_factor = 0.55
fset.factor_random = 0.5
# Физика
fset.physics_type = 'NEWTON'
fset.mass = 1.0
fset.particle_size = 10.0
fset.use_multiply_size_mass = False
# Веса эффекторов
ew = fset.effector_weights
ew.gravity = 0.0 ew.wind = 1.0
# Отображение и рендер
fset.draw_percentage = 100
fset.draw_method = 'RENDER'
fset.material = 1
fset.particle_size = 0.3
fset.render_type = 'BILLBOARD'
fset.render_step = 3
# Дочерние частицы
fset.child_type = 'SIMPLE'
fset.rendered_child_count = 50
fset.child_radius = 1.1
fset.child_roundness = 0.5 return fire
def createSmoke(emitter):
# Добавление второй системы частиц — дыма
bpy.context.scene.objects.active = emitter
bpy.ops.object.particle_system_add()
smoke = emitter.particle_systems[-1]
smoke.name = 'Smoke' sset = smoke.settings
# Эмиссия
sset.name = 'FireSettings'
sset.count = 100
sset.frame_start = 1
sset.frame_end = 100
sset.lifetime = 70
sset.lifetime_random = 0.2
sset.emit_from = 'FACE'
sset.use_render_emitter = False
sset.distribution = 'RAND'
# Скорость
sset.normal_factor = 0.0
sset.factor_random = 0.5
# Физика
sset.physics_type = 'NEWTON'
sset.mass = 2.5
sset.particle_size = 0.3
sset.use_multiply_size_mass = True
# Веса эффекторов
ew = sset.effector_weights
ew.gravity = 0.0
ew.wind = 1.0
# Отображение и рендер
sset.draw_percentage = 100
sset.draw_method = 'RENDER'
sset.material = 2
sset.particle_size = 0.5
sset.render_type = 'BILLBOARD'
sset.render_step = 3
# Дочерние частицы
sset.child_type = 'SIMPLE'
sset.rendered_child_count = 50
sset.child_radius = 1.6 return smoke
def createWind(origin):
# Создание ветра
bpy.ops.object.effector_add(
type='WIND',
enter_editmode=False,
location = origin - Vector((0,3,0)),
rotation = (-pi/2, 0, 0))
wind = bpy.context.object
# Настройки поля
fld = wind.field
fld.strength = 2.3
fld.noise = 3.2
fld.flow = 0.3
return wind
def createColorRamp(tex, values):
# Создание цветовой полосы
tex.use_color_ramp = True
ramp = tex.color_ramp
for n,value in enumerate(values):
elt = ramp.elements[n]
(pos, color) = value
elt.position = pos
elt.color = color
return
def createFlameTexture():
tex = bpy.data.textures.new('Flame', type = 'CLOUDS')
createColorRamp(tex, [(0.2, (1,0.5,0.1,1)), (0.8, (0.5,0,0,0))])
tex.noise_type = 'HARD_NOISE'
tex.noise_scale = 0.7
tex.noise_depth = 5
return tex
def createStencilTexture():
tex = bpy.data.textures.new('Stencil', type = 'BLEND')
tex.progression = 'SPHERICAL'
createColorRamp(tex, [(0.0, (0,0,0,0)), (0.85, (1,1,1,0.6))])
return tex
def createEmitTexture():
tex = bpy.data.textures.new('Emit',
type = 'BLEND')
tex.progression = 'LINEAR'
createColorRamp(tex, [(0.1, (1,1,0,1)), (0.3, (1,0,0,1))])
return tex
def createSmokeTexture():
tex = bpy.data.textures.new('Smoke', type = 'CLOUDS')
createColorRamp(tex, [(0.2, (0,0,0,1)), (0.6, (1,1,1,1))])
tex.noise_type = 'HARD_NOISE'
tex.noise_scale = 1.05
tex.noise_depth = 5
return tex
def createFireMaterial(textures, objects):
(flame, stencil, emit) = textures
(emitter, empty) = objects
mat = bpy.data.materials.new('Fire')
mat.specular_intensity = 0.0
mat.use_transparency = True
mat.transparency_method = 'Z_TRANSPARENCY'
mat.alpha = 0.0
mat.use_raytrace = False
mat.use_face_texture = True
mat.use_shadows = False
mat.use_cast_buffer_shadows = True
mtex = mat.texture_slots.add()
mtex.texture = emit
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = True
mtex = mat.texture_slots.add()
mtex.texture = stencil
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = False
mtex.use_map_emit = True
mtex.use_stencil = True
mtex = mat.texture_slots.add()
mtex.texture = flame
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = True
mtex.use_map_alpha = True
#mtex.object = empty
return mat
def createSmokeMaterial(textures, objects):
(smoke, stencil) = textures
(emitter, empty) = objects
mat = bpy.data.materials.new('Smoke')
mat.specular_intensity = 0.0
mat.use_transparency = True
mat.transparency_method = 'Z_TRANSPARENCY'
mat.alpha = 0.0
mat.use_raytrace = False
mat.use_face_texture = True
mat.use_shadows = True
mat.use_cast_buffer_shadows = True
mtex = mat.texture_slots.add()
mtex.texture = stencil
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = False
mtex.use_map_alpha = True
mtex.use_stencil = True
mtex = mat.texture_slots.add()
mtex.texture = smoke
mtex.texture_coords = 'OBJECT'
mtex.object = empty return mat
def run(origin):
emitter = createEmitter(origin)
#wind = createWind()
bpy.ops.object.add(type='EMPTY')
empty = bpy.context.object
fire = createFire(emitter)
flameTex = createFlameTexture()
stencilTex = createStencilTexture()
emitTex = createEmitTexture()
flameMat = createFireMaterial(
(flameTex, stencilTex, emitTex),
(emitter, empty))
emitter.data.materials.append(flameMat)
smoke = createSmoke(emitter
smokeTex = createSmokeTexture()
smokeMat = createSmokeMaterial(
(smokeTex, stencilTex), (emitter, empty))
emitter.data.materials.append(smokeMat)
return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run((0,0,0))
bpy.ops.screen.animation_play(reverse=False, sync=False)
Дым
Эта программа создает симуляцию дыма и присваивает воксельный материал.
#----------------------------------------------------------
)
# File smoke.py
# Создание дыма и материала дыма.
# Вдохновлен этим учебником Эндрю Прайса:
# http://www.blenderguru.com/introduction-to-smoke-simulation/
#----------------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector
from math import pi
def createDomain(origin):
# Добавление куба в качестве домена
bpy.ops.mesh.primitive_cube_add(location=origin
bpy.ops.transform.resize(value=(4, 4, 4))
domain = bpy.context.object domain.name = 'Domain'
# Добавление домену модификатора
dmod = domain.modifiers.new(name='Smoke', type='SMOKE')
dmod.smoke_type = 'DOMAIN'
dset = dmod.domain_settings
# Настройки домена
dset.resolution_max = 32
dset.alpha = -0.001
dset.beta = 2.0
dset.time_scale = 1.2
dset.vorticity = 2.0
dset.use_dissolve_smoke = True
dset.dissolve_speed = 80
dset.use_dissolve_smoke_log = True
dset.use_high_resolution = True
dset.show_high_resolution = True
# Веса эффекторов
ew = dset.effector_weights
ew.gravity = 0.4
ew.force = 0.8
return domain
def createFlow(origin):
# Добавление плоскости как потока
bpy.ops.mesh.primitive_plane_add(location = origin)
bpy.ops.transform.resize(value=(2, 2, 2))
flow = bpy.context.object flow.name = 'Flow'
# Добавление системы частиц дыма
pmod = flow.modifiers.new(name='SmokeParticles', type='PARTICLE_SYSTEM')
pmod.name = 'SmokeParticles'
psys = pmod.particle_system
psys.seed = 4711
# Настройки частиц
pset = psys.settings
pset.type = 'EMITTER'
pset.lifetime = 1
pset.emit_from = 'VOLUME'
pset.use_render_emitter = False
pset.render_type = 'NONE'
pset.normal_factor = 8.0
# Добавление модификатора дыма
smod = flow.modifiers.new(name='Smoke',
type='SMOKE')
smod.smoke_type = 'FLOW'
sfset = smod.flow_settings
# Настройки потока
sfset.use_outflow = False
sfset.temperature = 0.7
sfset.density = 0.8
sfset.initial_velocity = True
sfset.particle_system = psys
return flow
def createVortexEffector(origin):
bpy.ops.object.effector_add(type='VORTEX', location=origin)
vortex = bpy.context.object
return vortex
def createVoxelTexture(domain):
tex = bpy.data.textures.new('VoxelTex', type = 'VOXEL_DATA')
voxdata = tex.voxel_data
voxdata.file_format = 'SMOKE'
voxdata.domain_object = domain
return tex
def createVolumeMaterial(tex):
mat = bpy.data.materials.new('VolumeMat')
mat.type = 'VOLUME'
vol = mat.volume
vol.density = 0.0
vol.density_scale = 8.0
vol.scattering = 6.0
vol.asymmetry = 0.3
vol.emission = 0.3
vol.emission_color = (1,1,1)
vol.transmission_color = (0.9,0.2,0)
vol.reflection = 0.7
vol.reflection_color = (0.8,0.9,0)
# Для удаления эффекта пикселизации
vol.step_size = 0.05
# Добавление текстуры Voxel data
mtex = mat.texture_slots.add()
mtex.texture = tex
mtex.texture_coords = 'ORCO'
mtex.use_map_density = True
mtex.use_map_emission = True
mtex.use_map_scatter = False
mtex.use_map_reflect = True
mtex.use_map_color_emission = True
mtex.use_map_color_transmission = True
mtex.use_map_color_reflection = True
mtex.density_factor = 1.0
mtex.emission_factor = 0.2
mtex.scattering_factor = 0.2
mtex.reflection_factor = 0.3
mtex.emission_color_factor = 0.9
mtex.transmission_color_factor = 0.5
mtex.reflection_color_factor = 0.6
return mat
def addFloor(origin):
# Создание пола, который принимает прозрачные тени
bpy.ops.mesh.primitive_plane_add(
location = origin,
rotation = (0, 0, pi/4))
bpy.ops.transform.resize(value=(4, 4, 4))
bpy.ops.transform.resize(value=(2, 2, 2),
constraint_axis=(True, False, False),
constraint_orientation='LOCAL')
floor = bpy.context.object
mat = bpy.data.materials.new('Floor')
mat.use_transparent_shadows = True
floor.data.materials.append(mat)
return
def setupWorld():
scn = bpy.context.scene
# Синее blend (смешанное) небо
scn.world.use_sky_blend = True
scn.world.horizon_color = (0.25, 0.3, 0.4)
scn.world.zenith_color = (0, 0, 0.7)
# PAL 4:3 render
scn.render.resolution_x = 720
scn.render.resolution_y = 567
return
def run(origin):
domain = createDomain(origin)
flow = createFlow(origin-Vector((0,0,3.5)))
vortex = createVortexEffector(origin)
tex = createVoxelTexture(domain)
mat = createVolumeMaterial(tex)
domain.data.materials.append(mat)
return
if __name__ == "__main__":
for ob in bpy.context.scene.objects:
bpy.context.scene.objects.unlink(ob)
addFloor(Vector((0,0,-4)))
setupWorld()
# Освещение и камера
bpy.ops.object.lamp_add( type = 'POINT', location=(4,6,1))
bpy.ops.object.lamp_add( type = 'POINT', location=(-7,-5,0))
bpy.ops.object.camera_add(location=Vector((8,-8,3)),
rotation=(pi/3, 0, pi/6))
run(Vector((0,0,0)))
bpy.ops.screen.animation_play()
Симуляция твёрдого тела
Эта программа использует игровой движок Блендера для моделирования падения кучи объектов на землю. Анимации записываются и впоследствии могут быть воспроизведены.
#----------------------------------------------------------
# File pile.py
#----------------------------------------------------------
import bpy, mathutils, math, random
from mathutils import Vector NObjects = 7Seed = 444
def addSceneGameSettings(scn):
# Данные игровой сцены
sgdata = scn.game_settings
sgdata.fps = 25 sgdata.frequency = True
sgdata.material_mode = 'GLSL'
sgdata.show_debug_properties = True
sgdata.show_framerate_profile = True
sgdata.show_fullscreen = True
sgdata.show_physics_visualization = True
sgdata.use_animation_record = True return
def addMonkeyGameSettings(ob):
# Настройки игрового объекта
goset = ob.game
goset.physics_type = 'RIGID_BODY'
goset.use_actor = True
goset.use_ghost = False
goset.mass = 7.0
goset.damping = 0.0
goset.use_collision_bounds = True
goset.collision_bounds_type = 'BOX'
goset.show_actuators = True goset.show_controllers = True
goset.show_debug_state = True
goset.show_sensors = True goset.show_state_panel = True
return
def run(origin):
# Смена движка рендера с BLENDER_RENDER на BLENDER_GAME
bpy.context.scene.render.engine = 'BLENDER_GAME'
# Создание пола
bpy.ops.mesh.primitive_plane_add(location=origin)
bpy.ops.transform.resize(value=(20, 20, 20))
floor = bpy.context.object
mat = bpy.data.materials.new(name = 'FloorMaterial')
mat.diffuse_color = (0.5, 0.5, 0.5)
# Создание кучи объектов
objectType = ["cube", "ico_sphere", "monkey"]
objects = []
deg2rad = math.pi/180
random.seed(Seed)
for n in range(NObjects):
x = []
for i in range(3):
x.append( random.randrange(0, 360, 1) )
dx = 0.5*random.random()
dy = 0.5*random.random()
obType = objectType[ random.randrange(0, 3, 1) ]
fcn = eval("bpy.ops.mesh.primitive_%s_add" % obType)
fcn(location=origin+Vector((dx, dy, 3*n+3)),
rotation=deg2rad*Vector((x[0], x[1], x[2])))
ob = bpy.context.object objects.append( ob )
mat = bpy.data.materials.new(name='Material_%02d' % n) c = []
for j in range(3):
c.append( random.random() ) mat.diffuse_color = c
ob.data.materials.append(mat)
# Установка игровых настроек для пола
fset = floor.game
fset.physics_type = 'STATIC'
# Установка игровых настроек для объектов
for n in range(NObjects):
addMonkeyGameSettings(objects[n])
# Установка игровых настроек для сцены
scn = bpy.context.scene
addSceneGameSettings(scn)
scn.frame_start = 1
scn.frame_end = 200 return
if __name__ == "__main__":
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
run(Vector((0,0,0)))
bpy.ops.view3d.game_start()
Жидкости
Эта программа настраивает симуляцию жидкости с доменом, жидкостью, движущимся препятствием, притоком, оттоком, и тремя видами капель. Обратите внимание, что мы должны запечь симуляцию сначала, я не думаю, что это было необходимо.
Изображение кадра 57, после добавления нескольких материалов. Капли в основном отрендерены полностью, если они имеют низкую прозрачность, около alpha = 0,2.
#----------------------------------------------------------
)
# File fluid.py
#----------------------------------------------------------
import bpy, math
from mathutils import Vector
from math import pi
def createDomain(origin):
# Домен
bpy.ops.mesh.primitive_cube_add(location=origin)
bpy.ops.transform.resize(value=(4, 4, 4))
domain = bpy.context.object
domain.name = 'Domain'
bpy.ops.object.shade_smooth()
# Добавление модификатора домену
mod = domain.modifiers.new(name='FluidDomain', type='FLUID_SIMULATION')
# mod.settings is FluidSettings
mod.settings.type = 'DOMAIN'
# mod.settings now changed to DomainFluidSettings
settings = mod.settings
settings.use_speed_vectors = False
settings.simulation_scale = 3.0
settings.slip_type = 'FREESLIP'
settings.tracer_particles = 10
settings.generate_particles = 1.5
#settings.start_time = 0.0
#settings.end_time = 2.0
return domain
def createFluid(origin):
# Жидкость
bpy.ops.mesh.primitive_ico_sphere_add(
size=3.5,
subdivisions=1,
location=origin)
fluid = bpy.context.object
fluid.name = 'Fluid'
fluid.hide = True
fluid.hide_render = True
# Добавление модификатора жидкости
mod = fluid.modifiers.new(name='Fluid', type='FLUID_SIMULATION')
mod.settings.type = 'FLUID'
return fluid
def createObstacle(origin):
# Препятствие
bpy.ops.mesh.primitive_cylinder_add(
vertices=12,
radius=0.3,
depth=2,
cap_ends=True,
location=origin + Vector((0,0,-2.5)),
rotation=(pi/2, 0, 0))
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
obst = bpy.context.object
obst.name = 'Obstacle'
# Добавление модификатора препятствию
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
mod = obst.modifiers[-1]
mod.settings.type = 'OBSTACLE'
# Анимация препятствия
scn = bpy.context.scene
scn.frame_current = 1
bpy.ops.anim.keyframe_insert_menu(type='Rotation')
scn.frame_current = 26
bpy.ops.transform.rotate(value=(pi/2,), axis=(-0, -0, -1))
bpy.ops.anim.keyframe_insert_menu(type='Rotation')
scn.frame_current = 1
for fcu in obst.animation_data.action.fcurves:
fcu.extrapolation = 'LINEAR'
for kp in fcu.keyframe_points:
kp.interpolation = 'LINEAR'
return obst
def createInflow(origin):
# Приток
bpy.ops.mesh.primitive_circle_add(
radius=0.75,
fill=True,
location=origin+Vector((-3.9,0,3)),
rotation=(0, pi/2, 0))
inflow = bpy.context.object
inflow.name = 'Inflow'
# Добавление модификатора притоку
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
mod = inflow.modifiers[-1]
mod.settings.type = 'INFLOW'
settings = mod.settings
settings.inflow_velocity = (1.5,0,0
settings.volume_initialization = 'SHELL'
return inflow
def createOutflow(origin):
# Отток
bpy.ops.mesh.primitive_circle_add(
radius=0.75,
fill=True,
location=origin+Vector((3.9,0,-3)),
rotation=(0, -pi/2, 0))
outflow = bpy.context.object
outflow.name = 'Outflow'
# Добавление модификатора оттоку
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
mod = outflow.modifiers[-1]
mod.settings.type = 'OUTFLOW'
mod.settings.volume_initialization = 'SHELL'
return outflow
def createFluidParticle(name, origin, data):
# Частицы жидкости
bpy.ops.mesh.primitive_monkey_add(location=origin)
monkey = bpy.context.object
monkey.name = name
# Добавление модификатора жидкости-частиц
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
mod = monkey.modifiers[-1]
mod.settings.type = 'PARTICLE'
(drops, floats, tracer) = data
mod.settings.use_drops = drops
mod.settings.use_floats = floats
mod.settings.show_tracer = tracer
# Настройка типа частиц созданной системы частиц
psys = monkey.modifiers[-1].particle_system
psys.name = name+'Psys'
#psys.settings.name = name+'Pset'
return (mod.settings, None)
def run(origin):
domain = createDomain(origin)
fluid = createFluid(origin)
obst = createObstacle(origin)
inflow = createInflow(origin)
outflow = createOutflow(origin)
(settings, pset) = createFluidParticle('Drops',
origin+Vector((-2,7,0)), (True, False, False))
settings.particle_influence = 0.7
settings.alpha_influence = 0.3
(settings, pset) = createFluidParticle('Floats',
origin+Vector((0,7,0)), (False, True, False))
(settings, pset) = createFluidParticle('Tracer',
origin+Vector((2,7,0)), (False, False, True))
settings.particle_influence = 1.5
settings.alpha_influence = 1.2
return
if __name__ == "__main__":
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete() run(Vector((0,0,0)))
#bpy.ops.fluid.bake()