Dev:Py/Scripts/Cookbook/Code snippets/Simulations

提供: wiki
移動先: 案内検索

Simulations

In this section we access Blender's simulation capacity from Python. Several of the examples were inspired by the book Bounce, Tumble and Splash by Tony Mullen. However, most renders do not look as pretty as they do in Mullen's book, since the purpose of these note is not to find the optimal way to tweak parameters, but rather to illustrate how the can be tweaked from Python.

Particles

This program adds two particle systems.

Code Snippets Paticle.png

#---------------------------------------------------
# File particle.py
#---------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector, Matrix
from math import pi

def run(origo):
    # Add emitter mesh
    origin = Vector(origo)
    bpy.ops.mesh.primitive_plane_add(location=origin)
    emitter = bpy.context.object
    
    # --- Particle system 1: Falling and blowing drops ---
    
    # Add first particle system
    bpy.ops.object.particle_system_add()    
    psys1 = emitter.particle_systems[-1]
    psys1.name = 'Drops'

    # Emission    
    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)

    # Physics
    pset1.physics_type = 'NEWTON'
    pset1.mass = 2.5
    pset1.particle_size = 0.3
    pset1.use_multiply_size_mass = True

    # Effector weights
    ew = pset1.effector_weights
    ew.gravity = 1.0
    ew.wind = 1.0

    # Children
    pset1.child_nbr = 10
    pset1.rendered_child_count = 10
    pset1.child_type = 'SIMPLE'

    # Display and render
    pset1.draw_percentage = 100
    pset1.draw_method = 'CROSS'
    pset1.material = 1
    pset1.particle_size = 0.1    
    pset1.render_type = 'HALO'
    pset1.render_step = 3
    
    # ------------ Wind effector -----
    
    # Add wind effector
    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
    
    # Field settings
    fld = wind.field
    fld.strength = 2.3
    fld.noise = 3.2
    fld.flow = 0.3    

    # --- Particle system 2: Monkeys in the wind ----
    
    # Add monkey to be used as dupli object
    # Hide the monkey on layer 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

    #Add second particle system
    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'
    
    # Emission
    pset2.count = 4
    pset2.frame_start = 1
    pset2.frame_end = 50
    pset2.lifetime = 250
    pset2.emit_from = 'FACE'
    pset2.use_render_emitter = True
    
    # Velocity
    pset2.factor_random = 0.5

    # Physics
    pset2.physics_type = 'NEWTON'
    pset2.brownian_factor = 0.5

    # Effector weights
    ew = pset2.effector_weights
    ew.gravity = 0
    ew.wind = 0.2

    # Children
    pset2.child_nbr = 1
    pset2.rendered_child_count = 1
    pset2.child_size = 3
    pset2.child_type = 'SIMPLE'

    # Display and render
    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)

Hair

This program adds a sphere with hair. A strand shader is constructed for the hair.

Code Snippets Hair.png

#---------------------------------------------------
# File hair.py
#---------------------------------------------------
import bpy

def createHead(origin):
    # Add emitter mesh
    bpy.ops.mesh.primitive_ico_sphere_add(location=origin)
    ob = bpy.context.object
    bpy.ops.object.shade_smooth()
    
    # Create scalp vertex group, and add verts and weights
    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):
    # Some material for the skin
    skinmat = bpy.data.materials.new('Skin')
    skinmat.diffuse_color = (0.6,0.3,0)
    
    # Strand material for hair
    hairmat = bpy.data.materials.new('Strand')
    hairmat.diffuse_color = (0.2,0.04,0.0)
    hairmat.specular_intensity = 0

    # Transparency
    hairmat.use_transparency = True
    hairmat.transparency_method = 'Z_TRANSPARENCY'
    hairmat.alpha = 0

    # Strand. Must use Blender units before sizes are pset.
    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

    # Texture
    tex = bpy.data.textures.new('Blend', type = 'BLEND')
    tex.progression = 'LINEAR'
    tex.use_flip_axis = 'HORIZONTAL'

    # Create a color ramp for color and alpha
    tex.use_color_ramp = True
    tex.color_ramp.interpolation = 'B_SPLINE'
    # Points in color ramp: (pos, rgba)
    # Have not figured out how to add points to ramp
    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

    # Add blend texture to hairmat
    mtex = hairmat.texture_slots.add()
    mtex.texture = tex
    mtex.texture_coords = 'STRAND'
    mtex.use_map_color_diffuse = True 
    mtex.use_map_alpha = True 
    
    # Add materials to mesh    
    ob.data.materials.append(skinmat)    # Material 1 = Skin
    ob.data.materials.append(hairmat)    # Material 2 = Strand
    return    

def createHair(ob):    
    # Create hair particle system
    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'

    # Emission
    pset.count = 40
    pset.hair_step = 7
    pset.emit_from = 'FACE'

    # Render
    pset.material = 2
    pset.use_render_emitter = True
    pset.render_type = 'PATH'
    pset.use_strand_primitive = True
    pset.use_hair_bspline = True

    # Children
    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))

Editable hair

This program adds a sphere with editable hair from given hair guides. If we toggle to edit mode, all strands become straight, i.e. the editing is lost. This is prevented if you toggle to particle mode, select the object, and toggle back into object mode. Unfortunately, I have not found a way to do this with a script.

Code Snippets EditHair.png

#---------------------------------------------------
# File edit_hair.py
# Has flaws, but may be of interest anyway.
#---------------------------------------------------
import bpy

def createHead():
    # Add emitter mesh
    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])

    # Create hair particle system
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.particle_system_add()
    psys = ob.particle_systems.active
    psys.name = 'Hair'

    # Particle settings
    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

    # Children
    pset.child_type = 'SIMPLE'
    pset.child_nbr = 6
    pset.rendered_child_count = 300
    pset.child_length = 1.0
    pset.child_length_threshold = 0.0

    # Disconnect hair and switch to particle edit mode
    bpy.ops.particle.disconnect_hair(all=True)
    bpy.ops.particle.particle_edit_toggle()

    # Set all hair-keys
    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

    # Toggle particle edit mode
    bpy.ops.particle.select_all(action='SELECT')
    bpy.ops.particle.particle_edit_toggle()

    # Connect hair to mesh
    # Segmentation violation during render if this line is absent.
    bpy.ops.particle.connect_hair(all=True)

    # Unfortunately, here a manual step appears to be needed:
    # 1. Toggle to particle mode
    # 2. Touch object with a brush
    # 3. Toggle to object mode
    # 4. Toggle to edit mode
    # 5. Toggle to object mode
    # This should correspond to the code below, but fails due to
    # wrong context
    '''
    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

# Hair guides. Four hair with five points each.
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))

Cloth

This program adds a plane with a cloth modifier. The plane is parented to a hoop which moves downward, where it meets a sphere obstacle. The influence of the cloth modifier is controlled by a vertex group, which means that the corners move with the hoop while the middle is deformed by the obstacle. The plane is given a material with stress-mapped transparency.

Code Snippets Cloth.png

#----------------------------------------------------------
# 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

    # Add a sphere acting as a collision object
    bpy.ops.mesh.primitive_ico_sphere_add(location=origin)
    sphere = bpy.context.object
    bpy.ops.object.shade_smooth()

    # Add a collision modifier to the sphere    
    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')

    # Add ring
    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

    # Add a plane over the sphere and parent it to ring
    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

    # Create vertex group. Object must not be active?
    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')

    # Reactivate plane
    scn.objects.active = plane
    
    # Add cloth modifier
    cloth = plane.modifiers.new(name='Cloth', type='CLOTH')
    cset = cloth.settings
    cset.use_pin_cloth = True
    cset.vertex_group_mass = 'Group'
    # Silk presets, copied from "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

    # Smooth shading
    plane.select = True
    bpy.ops.object.shade_smooth()
    bpy.ops.object.modifier_add(type='SUBSURF')

    # Blend texture
    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 material
    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'
    
    # Add material to plane
    plane.data.materials.append(mat)    

    # Animate ring 
    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()

Softbodies

This program adds a sphere with a softbody modifier and a plane obstacle.

Code Snippets Softbody.png

#----------------------------------------------------------
# File softbody.py
#----------------------------------------------------------
import bpy
import mathutils
from mathutils import Vector

def run(origin):
    # Add materials
    red = bpy.data.materials.new('Red')
    red.diffuse_color = (1,0,0)
    blue = bpy.data.materials.new('Blue')
    blue.diffuse_color = (0,0,1)
    
    # Add a cone
    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')
    
    # Weirdly, need new mesh which is a copy of
    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)
    
    # Set faces smooth
    for f in me2.faces:
        f.use_smooth = True

    # Add new object and make it active
    ob2 = bpy.data.objects.new('Drop', me2)
    scn = bpy.context.scene
    scn.objects.link(ob2)
    scn.objects.unlink(ob1)
    scn.objects.active = ob2

    # Add vertex groups
    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)
    
    # Add a softbody modifier
    mod = ob2.modifiers.new(name='SoftBody', type='SOFT_BODY')
    sbset = mod.settings
    
    # Soft body
    sbset.friction = 0.6
    sbset.speed = 0.4
    sbset.mass = 8.1
    
    # Goal
    sbset.goal_default = 0.7
    sbset.goal_spring = 0.3
    sbset.goal_friction = 0.0
    sbset.vertex_group_goal = 'Top'
    
    # Soft body edges
    sbset.pull = 0.6
    sbset.push = 0.1
    sbset.bend = 0.1
    sbset.aerodynamics_type = 'LIFT_FORCE'
    sbset.aero = 0.5
    
    # Add a vortex
    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'
    
    # Add collision plane
    # Warning. Collision objects make simulation very slow!
    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)

Cloth, softbodies and displacement textures

This program illustrates three different methods to make a waving flag: with a cloth modifier, with a softbody modifier, and with animated displacement textures.

Code Snippets Flags.png

#----------------------------------------------------------
# File flags.py
# Creates a softbody flag and a cloth flag in the wind.
# Update to API rev. 36816
#----------------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector
from math import pi

# Flag size, global variables
xmax = 40
zmax = 24
ds = 2.0/xmax

def makeFlag(name, origin, invert):
    # Add new mesh that will be the flag    

    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
    
    # Build flag mesh
    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
    
    # Add vertex groups
    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
    
    # Soft body
    sbset.friction = 0.3
    sbset.speed = 1.4
    sbset.mass = 0.9
    
    # Goal
    sbset.goal_default = 0.3
    sbset.goal_spring = 0.1
    sbset.goal_friction = 0.1
    sbset.vertex_group_goal = 'Pole'
    
    # Soft body edges
    sbset.pull = 0.1
    sbset.push = 0.1
    sbset.bend = 0.1
    sbset.aerodynamics_type = 'LIFT_FORCE'
    sbset.aero = 0.5
    
    #Effector weights
    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'

    #Effector weights
    ew = cset.effector_weights
    ew.gravity = 0.1
    ew.wind = 1.0
    return
        
    
def addWindEffector(origin):    
    # Add wind effector
    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):        
    # Flag texture
    tex = bpy.data.textures.new('Flag', type = 'WOOD')
    tex.noise_basis_2 = 'TRI'
    tex.wood_type = 'RINGS'
    
    # Create material
    mat = bpy.data.materials.new(name)
    mat.diffuse_color = color1
    
    # Add texture slot for color texture
    mtex = mat.texture_slots.add()
    mtex.texture = tex
    mtex.texture_coords = 'ORCO'
    mtex.use_map_color_diffuse = True
    mtex.color = color2
    
    # Add material to flags
    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
    # Store texture in material for easy access. Not really necessary
    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):
    # Create flags and poles
    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)))

    # Materials
    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)

    # Add modifiers and wind
    addSoftBodyModifier(flag1)
    addClothModifier(flag2)
    addWindEffector(origin+Vector((-1,-2,0)))
    
    # Create displacement
    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()

Particles and explode modifier

A bullet with an invisible particle system is fired at a crystal ball. The ball is shattered and the pieces fall to the floot.

The effect is achieved by giving the ball an explode modifier which is triggered by a particle system. The idea was to make this into a reaction particle system, which is triggered by the particle system of the bullet. However, reactor particles are apparently not yet implemented in Blender 2.5, so the balls particles are set to emit at a specific time instead.

Code Snippets Crystal.png

#----------------------------------------------------------
# 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

    # Floor material
    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):
    # ----------- Materials
    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 - a small sphere
    bullet = addSphere('Bullet', 0.2, origin)
    bullet.data.materials.append(grey)
    
    # Animate bullet
    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 particle system for bullet
    
    bpy.ops.object.particle_system_add()    
    trail = bullet.particle_systems[0]
    trail.name = 'Trail'
    fset = trail.settings
    # Emission    
    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
    # Velocity
    fset.normal_factor = 1.0
    fset.factor_random = 0.5    
    # Physics
    fset.physics_type = 'NEWTON'
    fset.mass = 0
    # Set all effector weights to zero
    ew = fset.effector_weights
    ew.gravity = 0.0
    # Don't render
    fset.draw_method = 'DOT'
    fset.render_type = 'NONE'
    
    # -------------- Ball    
    ball = addSphere('Ball', 1.0, origin)
    ball.data.materials.append(red)
    
    # Particle system
    bpy.ops.object.particle_system_add()    
    react = ball.particle_systems[0]
    react.name = 'React'
    fset = react.settings
    # Emission    
    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
    # Velocity
    fset.normal_factor = 5.0
    fset.factor_random = 2.5    
    # Physics
    fset.physics_type = 'NEWTON'
    fset.mass = 1.0
    # Don't render
    fset.draw_method = 'CROSS'
    fset.render_type = 'NONE'
    
    # Explode modifier
    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

    # ----------- Hidden floor with collision modifier
    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()

    # Camera, lights
    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)))

Particle fire and smoke

This program adds two particle systems for fire and smoke. The particles render as billboards with procedural textures.

Code Snippets Fire.png

#---------------------------------------------------
# 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):    
    # Add first particle system
    bpy.context.scene.objects.active = emitter
    bpy.ops.object.particle_system_add()    
    fire = emitter.particle_systems[-1]
    fire.name = 'Fire'
    fset = fire.settings

    # Emission    
    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)

    # Velocity
    fset.normal_factor = 0.55
    fset.factor_random = 0.5
    
    # Physics
    fset.physics_type = 'NEWTON'
    fset.mass = 1.0
    fset.particle_size = 10.0
    fset.use_multiply_size_mass = False

    # Effector weights
    ew = fset.effector_weights
    ew.gravity = 0.0
    ew.wind = 1.0

    # Display and render
    fset.draw_percentage = 100
    fset.draw_method = 'RENDER'
    fset.material = 1
    fset.particle_size = 0.3
    fset.render_type = 'BILLBOARD'
    fset.render_step = 3

    # Children
    fset.child_type = 'SIMPLE'
    fset.rendered_child_count = 50
    fset.child_radius = 1.1
    fset.child_roundness = 0.5
    return fire
        
def createSmoke(emitter):    
    # Add second particle system
    bpy.context.scene.objects.active = emitter
    bpy.ops.object.particle_system_add()    
    smoke = emitter.particle_systems[-1]
    smoke.name = 'Smoke'
    sset = smoke.settings

    # Emission    
    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'

    # Velocity
    sset.normal_factor = 0.0
    sset.factor_random = 0.5
    
    # Physics
    sset.physics_type = 'NEWTON'
    sset.mass = 2.5
    sset.particle_size = 0.3
    sset.use_multiply_size_mass = True

    # Effector weights
    ew = sset.effector_weights
    ew.gravity = 0.0
    ew.wind = 1.0

    # Display and render
    sset.draw_percentage = 100
    sset.draw_method = 'RENDER'
    sset.material = 2
    sset.particle_size = 0.5    
    sset.render_type = 'BILLBOARD'
    sset.render_step = 3

    # Children
    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
    
    # Field settings
    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)

Smoke

This program sets up a smoke simulation and assigns a voxel material.

Code Snippets Smoke.png

#----------------------------------------------------------
# File smoke.py
# Creates smoke and smoke material. 
# Heavily inspired by Andrew Price's tutorial at
# http://www.blenderguru.com/introduction-to-smoke-simulation/
#----------------------------------------------------------
import bpy, mathutils, math
from mathutils import Vector
from math import pi

def createDomain(origin):
    # Add cube as domain
    bpy.ops.mesh.primitive_cube_add(location=origin)
    bpy.ops.transform.resize(value=(4, 4, 4))
    domain = bpy.context.object
    domain.name = 'Domain'

    # Add domain modifier
    dmod = domain.modifiers.new(name='Smoke', type='SMOKE')
    dmod.smoke_type = 'DOMAIN'
    dset = dmod.domain_settings

    # 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

    # Effector weights
    ew = dset.effector_weights
    ew.gravity = 0.4
    ew.force = 0.8
    return domain

def createFlow(origin):
    # Add plane as flow
    bpy.ops.mesh.primitive_plane_add(location = origin)
    bpy.ops.transform.resize(value=(2, 2, 2))
    flow = bpy.context.object
    flow.name = 'Flow'

    # Add smoke particle system
    pmod = flow.modifiers.new(name='SmokeParticles', type='PARTICLE_SYSTEM')
    pmod.name = 'SmokeParticles'
    psys = pmod.particle_system
    psys.seed = 4711

    # Particle settings
    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

    # Add smoke modifier
    smod = flow.modifiers.new(name='Smoke', type='SMOKE')
    smod.smoke_type = 'FLOW'
    sfset = smod.flow_settings

    # 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)
    # To remove pixelation effects
    vol.step_size = 0.05

    # Add voxdata texture
    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):
    # Create floor that receives transparent shadows
    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
    # Blue blend sky
    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()
    # Lights and camera
    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()

Rigid body simulation

This program uses the Blender game engine to simulate a pile of objects falling to the ground. The animation is recorded and can be replayed afterwards.

Code Snippets Pile.png

#----------------------------------------------------------
# File pile.py
#----------------------------------------------------------
import bpy, mathutils, math, random
from mathutils import Vector

NObjects = 7
Seed = 444

def addSceneGameSettings(scn):
    # SceneGameData
    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):        
    # GameObjectSettings
    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):
    # Change render engine from BLENDER_RENDER to BLENDER_GAME
    bpy.context.scene.render.engine = 'BLENDER_GAME'

    # Create floor
    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)

    # Create a pile of objects
    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)

    # Set game settings for floor        
    fset = floor.game
    fset.physics_type = 'STATIC'

    # Set game settings for monkeys
    for n in range(NObjects):
        addMonkeyGameSettings(objects[n])
    
    # Set game settings for scene
    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()

Fluids

This program sets up a fluid simulation with a domain, a fluid, a moving obstacle, an inflow, an outflow, and three kinds of drops. Note that we must bake the simulation first; I don't think that used to be necessary.

The pictures are from frame 57, after some materials have been added. The drop dominate the render completely unless they are given a low opacity, around alpha = 0.2.

Code Snippets Fluid1.png

Code Snippets Fluid.png

#----------------------------------------------------------
# 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()
    # Add domain modifier
    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
    # Add fluid modifier. 
    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'
    # Add fluid modifier
    bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
    mod = obst.modifiers[-1]
    mod.settings.type = 'OBSTACLE'

    # Animate 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'
    # Add fluid modifier
    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'
    # Add fluid modifier
    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
    # Add fluid modifier
    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

    # Setting type to particle created a particle system
    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()