Dev:Py/Scripts/Cookbook/Code snippets/Simulations
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.
#---------------------------------------------------
# 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.
#---------------------------------------------------
# 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.
#---------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#---------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#----------------------------------------------------------
# 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.
#----------------------------------------------------------
# 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()