Книга: Введение в написание скриптов на Питоне для Блендера 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()

Оглавление книги


Генерация: 0.069. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз