1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 from mathutils import Vector
23 from bpy.types import Operator
24 from bpy.props import (BoolProperty,
32 def object_ensure_material(obj, mat_name):
33 """ Use an existing material or add a new one.
36 for mat_slot in obj.material_slots:
37 mat = mat_slot.material
41 mat = bpy.data.materials.new(mat_name)
43 mat_slot.material = mat
45 obj.data.materials.append(mat)
49 class QuickFur(Operator):
50 bl_idname = "object.quick_fur"
51 bl_label = "Quick Fur"
52 bl_options = {'REGISTER', 'UNDO'}
54 density = EnumProperty(items=(
55 ('LIGHT', "Light", ""),
56 ('MEDIUM', "Medium", ""),
57 ('HEAVY', "Heavy", "")),
62 view_percentage = IntProperty(name="View %",
63 default=10, min=1, max=100, soft_min=1, soft_max=100)
65 length = FloatProperty(name="Length",
66 default=0.1, min=0.001, max=100, soft_min=0.01, soft_max=10)
68 def execute(self, context):
69 fake_context = bpy.context.copy()
70 mesh_objects = [obj for obj in context.selected_objects
71 if obj.type == 'MESH']
74 self.report({'ERROR'}, "Select at least one mesh object.")
77 mat = bpy.data.materials.new("Fur Material")
78 mat.strand.tip_size = 0.25
79 mat.strand.blend_distance = 0.5
81 for obj in mesh_objects:
82 fake_context["object"] = obj
83 bpy.ops.object.particle_system_add(fake_context)
85 psys = obj.particle_systems[-1]
86 psys.settings.type = 'HAIR'
88 if self.density == 'LIGHT':
89 psys.settings.count = 100
90 elif self.density == 'MEDIUM':
91 psys.settings.count = 1000
92 elif self.density == 'HEAVY':
93 psys.settings.count = 10000
95 psys.settings.child_nbr = self.view_percentage
96 psys.settings.hair_length = self.length
97 psys.settings.use_strand_primitive = True
98 psys.settings.use_hair_bspline = True
99 psys.settings.child_type = 'INTERPOLATED'
101 obj.data.materials.append(mat)
102 obj.particle_systems[-1].settings.material = \
103 len(obj.data.materials)
108 class QuickExplode(Operator):
109 bl_idname = "object.quick_explode"
110 bl_label = "Quick Explode"
111 bl_options = {'REGISTER', 'UNDO'}
113 style = EnumProperty(items=(
114 ('EXPLODE', "Explode", ""),
115 ('BLEND', "Blend", "")),
116 name="Explode Style",
120 amount = IntProperty(name="Amount of pieces",
121 default=100, min=2, max=10000, soft_min=2, soft_max=10000)
123 frame_duration = IntProperty(name="Duration",
124 default=50, min=1, max=300000, soft_min=1, soft_max=10000)
126 frame_start = IntProperty(name="Start Frame",
127 default=1, min=1, max=300000, soft_min=1, soft_max=10000)
129 frame_end = IntProperty(name="End Frame",
130 default=10, min=1, max=300000, soft_min=1, soft_max=10000)
132 velocity = FloatProperty(name="Outwards Velocity",
133 default=1, min=0, max=300000, soft_min=0, soft_max=10)
135 fade = BoolProperty(name="Fade",
136 description="Fade the pieces over time.",
139 def execute(self, context):
140 fake_context = bpy.context.copy()
141 obj_act = context.active_object
143 if obj_act is None or obj_act.type != 'MESH':
144 self.report({'ERROR'}, "Active object is not a mesh")
147 mesh_objects = [obj for obj in context.selected_objects
148 if obj.type == 'MESH' and obj != obj_act]
149 mesh_objects.insert(0, obj_act)
151 if self.style == 'BLEND' and len(mesh_objects) != 2:
152 self.report({'ERROR'}, "Select two mesh objects")
154 elif not mesh_objects:
155 self.report({'ERROR'}, "Select at least one mesh object")
158 for obj in mesh_objects:
159 if obj.particle_systems:
160 self.report({'ERROR'},
161 "Object %r already has a "
162 "particle system" % obj.name)
167 tex = bpy.data.textures.new("Explode fade", 'BLEND')
168 tex.use_color_ramp = True
170 if self.style == 'BLEND':
171 tex.color_ramp.elements[0].position = 0.333
172 tex.color_ramp.elements[1].position = 0.666
174 tex.color_ramp.elements[0].color[3] = 1.0
175 tex.color_ramp.elements[1].color[3] = 0.0
177 if self.style == 'BLEND':
178 from_obj = mesh_objects[1]
179 to_obj = mesh_objects[0]
181 for obj in mesh_objects:
182 fake_context["object"] = obj
183 bpy.ops.object.particle_system_add(fake_context)
185 settings = obj.particle_systems[-1].settings
186 settings.count = self.amount
187 settings.frame_start = self.frame_start
188 settings.frame_end = self.frame_end - self.frame_duration
189 settings.lifetime = self.frame_duration
190 settings.normal_factor = self.velocity
191 settings.render_type = 'NONE'
193 explode = obj.modifiers.new(name='Explode', type='EXPLODE')
194 explode.use_edge_cut = True
197 explode.show_dead = False
198 uv = obj.data.uv_textures.new("Explode fade")
199 explode.particle_uv = uv.name
201 mat = object_ensure_material(obj, "Explode Fade")
203 mat.use_transparency = True
204 mat.use_transparent_shadows = True
206 mat.specular_alpha = 0.0
208 tex_slot = mat.texture_slots.add()
210 tex_slot.texture = tex
211 tex_slot.texture_coords = 'UV'
212 tex_slot.uv_layer = uv.name
214 tex_slot.use_map_alpha = True
216 if self.style == 'BLEND':
218 tex_slot.alpha_factor = -1.0
219 elem = tex.color_ramp.elements[1]
220 elem.color = mat.diffuse_color
222 elem = tex.color_ramp.elements[0]
223 elem.color = mat.diffuse_color
225 tex_slot.use_map_color_diffuse = False
227 if self.style == 'BLEND':
228 settings.physics_type = 'KEYED'
229 settings.use_emit_random = False
230 settings.rotation_mode = 'NOR'
232 psys = obj.particle_systems[-1]
234 fake_context["particle_system"] = obj.particle_systems[-1]
235 bpy.ops.particle.new_target(fake_context)
236 bpy.ops.particle.new_target(fake_context)
239 psys.targets[1].object = to_obj
241 psys.targets[0].object = from_obj
242 settings.normal_factor = -self.velocity
243 explode.show_unborn = False
244 explode.show_dead = True
246 settings.factor_random = self.velocity
247 settings.angular_velocity_factor = self.velocity / 10.0
251 def invoke(self, context, event):
252 self.frame_start = context.scene.frame_current
253 self.frame_end = self.frame_start + self.frame_duration
254 return self.execute(context)
257 def obj_bb_minmax(obj, min_co, max_co):
258 for i in range(0, 8):
259 bb_vec = obj.matrix_world * Vector(obj.bound_box[i])
261 min_co[0] = min(bb_vec[0], min_co[0])
262 min_co[1] = min(bb_vec[1], min_co[1])
263 min_co[2] = min(bb_vec[2], min_co[2])
264 max_co[0] = max(bb_vec[0], max_co[0])
265 max_co[1] = max(bb_vec[1], max_co[1])
266 max_co[2] = max(bb_vec[2], max_co[2])
269 class QuickSmoke(Operator):
270 bl_idname = "object.quick_smoke"
271 bl_label = "Quick Smoke"
272 bl_options = {'REGISTER', 'UNDO'}
274 style = EnumProperty(
275 items=(('STREAM', "Stream", ""),
276 ('PUFF', "Puff", ""),
277 ('FIRE', "Fire", ""),
284 show_flows = BoolProperty(
285 name="Render Smoke Objects",
286 description="Keep the smoke objects visible during rendering.",
290 def execute(self, context):
291 fake_context = bpy.context.copy()
292 mesh_objects = [obj for obj in context.selected_objects
293 if obj.type == 'MESH']
294 min_co = Vector((100000.0, 100000.0, 100000.0))
298 self.report({'ERROR'}, "Select at least one mesh object.")
301 for obj in mesh_objects:
302 fake_context["object"] = obj
303 # make each selected object a smoke flow
304 bpy.ops.object.modifier_add(fake_context, type='SMOKE')
305 obj.modifiers[-1].smoke_type = 'FLOW'
307 psys = obj.particle_systems[-1]
308 if self.style == 'PUFF':
309 psys.settings.frame_end = psys.settings.frame_start
310 psys.settings.emit_from = 'VOLUME'
311 psys.settings.distribution = 'RAND'
312 elif self.style == 'FIRE':
313 psys.settings.effector_weights.gravity = -1
314 psys.settings.lifetime = 5
315 psys.settings.count = 100000
317 obj.modifiers[-2].flow_settings.initial_velocity = True
318 obj.modifiers[-2].flow_settings.temperature = 2
320 psys.settings.use_render_emitter = self.show_flows
321 if not self.show_flows:
322 obj.draw_type = 'WIRE'
324 # store bounding box min/max for the domain object
325 obj_bb_minmax(obj, min_co, max_co)
327 # add the smoke domain object
328 bpy.ops.mesh.primitive_cube_add()
329 obj = context.active_object
330 obj.name = "Smoke Domain"
332 # give the smoke some room above the flows
333 obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, 1.0))
334 obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0))
337 bpy.ops.object.modifier_add(type='SMOKE')
338 obj.modifiers[-1].smoke_type = 'DOMAIN'
339 if self.style == 'FIRE':
340 obj.modifiers[-1].domain_settings.use_dissolve_smoke = True
341 obj.modifiers[-1].domain_settings.dissolve_speed = 20
342 obj.modifiers[-1].domain_settings.use_high_resolution = True
344 # create a volume material with a voxel data texture for the domain
345 bpy.ops.object.material_slot_add()
347 mat = bpy.data.materials.new("Smoke Domain Material")
348 obj.material_slots[0].material = mat
350 mat.volume.density = 0
351 mat.volume.density_scale = 5
353 tex = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA')
354 tex.voxel_data.domain_object = obj
356 tex_slot = mat.texture_slots.add()
357 tex_slot.texture = tex
358 tex_slot.use_map_color_emission = False
359 tex_slot.use_map_density = True
361 # for fire add a second texture for emission and emission color
362 if self.style == 'FIRE':
363 mat.volume.emission = 5
364 tex = bpy.data.textures.new("Smoke Heat", 'VOXEL_DATA')
365 tex.voxel_data.domain_object = obj
366 tex.use_color_ramp = True
368 tex_slot = mat.texture_slots.add()
369 tex_slot.texture = tex
371 ramp = tex.color_ramp
373 elem = ramp.elements.new(0.333)
374 elem.color[0] = elem.color[3] = 1
375 elem.color[1] = elem.color[2] = 0
377 elem = ramp.elements.new(0.666)
378 elem.color[0] = elem.color[1] = elem.color[3] = 1
381 mat.texture_slots[1].use_map_emission = True
382 mat.texture_slots[1].blend_type = 'MULTIPLY'
387 class QuickFluid(Operator):
388 bl_idname = "object.quick_fluid"
389 bl_label = "Quick Fluid"
390 bl_options = {'REGISTER', 'UNDO'}
392 style = EnumProperty(
393 items=(('INFLOW', "Inflow", ""),
394 ('BASIC', "Basic", ""),
400 initial_velocity = FloatVectorProperty(
401 name="Initial Velocity",
402 description="Initial velocity of the fluid",
403 default=(0.0, 0.0, 0.0),
408 show_flows = BoolProperty(
409 name="Render Fluid Objects",
410 description="Keep the fluid objects visible during rendering.",
413 start_baking = BoolProperty(
414 name="Start Fluid Bake",
415 description=("Start baking the fluid immediately "
416 "after creating the domain object"),
420 def execute(self, context):
421 fake_context = bpy.context.copy()
422 mesh_objects = [obj for obj in context.selected_objects
423 if (obj.type == 'MESH' and not 0.0 in obj.dimensions)]
424 min_co = Vector((100000, 100000, 100000))
425 max_co = Vector((-100000, -100000, -100000))
428 self.report({'ERROR'}, "Select at least one mesh object.")
431 for obj in mesh_objects:
432 fake_context["object"] = obj
433 # make each selected object a fluid
434 bpy.ops.object.modifier_add(fake_context, type='FLUID_SIMULATION')
436 # fluid has to be before constructive modifiers,
437 # so it might not be the last modifier
438 for mod in obj.modifiers:
439 if mod.type == 'FLUID_SIMULATION':
442 if self.style == 'INFLOW':
443 mod.settings.type = 'INFLOW'
444 mod.settings.inflow_velocity = self.initial_velocity.copy()
446 mod.settings.type = 'FLUID'
447 mod.settings.initial_velocity = self.initial_velocity.copy()
449 obj.hide_render = not self.show_flows
450 if not self.show_flows:
451 obj.draw_type = 'WIRE'
453 # store bounding box min/max for the domain object
454 obj_bb_minmax(obj, min_co, max_co)
456 # add the fluid domain object
457 bpy.ops.mesh.primitive_cube_add()
458 obj = context.active_object
459 obj.name = "Fluid Domain"
461 # give the fluid some room below the flows
462 # and scale with initial velocity
463 v = 0.5 * self.initial_velocity
464 obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v
465 obj.scale = (0.5 * (max_co - min_co) +
466 Vector((1.0, 1.0, 2.0)) +
467 Vector((abs(v[0]), abs(v[1]), abs(v[2])))
471 bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
472 obj.modifiers[-1].settings.type = 'DOMAIN'
474 # make the domain smooth so it renders nicely
475 bpy.ops.object.shade_smooth()
477 # create a ray-transparent material for the domain
478 bpy.ops.object.material_slot_add()
480 mat = bpy.data.materials.new("Fluid Domain Material")
481 obj.material_slots[0].material = mat
483 mat.specular_intensity = 1
484 mat.specular_hardness = 100
485 mat.use_transparency = True
487 mat.transparency_method = 'RAYTRACE'
488 mat.raytrace_transparency.ior = 1.33
489 mat.raytrace_transparency.depth = 4
491 if self.start_baking: