From 22abd53c7ee99274ed93ba188e9f59e740443088 Mon Sep 17 00:00:00 2001 From: Janne Karhu Date: Wed, 30 Mar 2011 10:29:32 +0000 Subject: Quick effects operators: * A couple of operators to quickly create effects that would otherwise take some time to set up. * Nice to use for demoing functionality or as a starting point for more complex effects. * "Make Fur" - Gives every selected mesh object particle fur with a desired density and length. * "Make Smoke" - Makes each selected object a smoke emitter and creates a new domain object around the emitters with the correct material to render the smoke. ** Has style options for "stream": constant smoke flow, "puff": only create smoke once from the volume of the emitter object, "fire": enable high resolution smoke and set a secondary fire color texture for the domain object. * "Make Fluid" - Makes every selected object a fluid object (normal/inflow) and has the option to start fluid baking immediately. * This should provide a nice base for extending these / adding more operators for different effects. --- release/scripts/startup/bl_operators/__init__.py | 1 + .../startup/bl_operators/object_quick_effects.py | 310 +++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 release/scripts/startup/bl_operators/object_quick_effects.py (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 2a42cfbacb8..d5f7a63366a 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -31,6 +31,7 @@ _modules = ( "object_align", "object", "object_randomize_transform", + "object_quick_effects", "presets", "screen_play_rendered_anim", "sequencer", diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py new file mode 100644 index 00000000000..332b7ebae42 --- /dev/null +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -0,0 +1,310 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import mathutils +from mathutils import Vector +import bpy +from bpy.props import BoolProperty, EnumProperty, IntProperty, FloatProperty, FloatVectorProperty + +class MakeFur(bpy.types.Operator): + bl_idname = "object.make_fur" + bl_label = "Make Fur" + bl_options = {'REGISTER', 'UNDO'} + + density = EnumProperty(items=( + ('LIGHT', "Light", ""), + ('MEDIUM', "Medium", ""), + ('HEAVY', "Heavy", "")), + name="Fur Density", + description="", + default='MEDIUM') + + view_percentage = IntProperty(name="View %", + default=10, min=1, max=100, soft_min=1, soft_max=100) + + length = FloatProperty(name="Length", + default=0.1, min=0.001, max=100, soft_min=0.01, soft_max=10) + + def execute(self, context): + count = 0 + for ob in context.selected_objects: + + if(ob == None or ob.type != 'MESH'): + continue + + count += 1 + + context.scene.objects.active = ob + + bpy.ops.object.particle_system_add() + + psys = ob.particle_systems[-1] + psys.settings.type = 'HAIR' + + if self.density == 'LIGHT': + psys.settings.count = 100 + elif self.density == 'MEDIUM': + psys.settings.count = 1000 + elif self.density == 'HEAVY': + psys.settings.count = 10000 + + psys.settings.child_nbr = self.view_percentage + psys.settings.hair_length = self.length + psys.settings.use_strand_primitive = True + psys.settings.use_hair_bspline = True + psys.settings.child_type = 'INTERPOLATED' + + if count == 0: + self.report({'ERROR'}, "Select at least one mesh object.") + return {'CANCELLED'} + + return {'FINISHED'} + +class MakeSmoke(bpy.types.Operator): + bl_idname = "object.make_smoke" + bl_label = "Make Smoke" + bl_options = {'REGISTER', 'UNDO'} + + style = EnumProperty(items=( + ('STREAM', "Stream", ""), + ('PUFF', "Puff", ""), + ('FIRE', "Fire", "")), + name="Smoke Style", + description="", + default='STREAM') + + show_flows = BoolProperty(name="Render Smoke Objects", + description="Keep the smoke objects visible during rendering.", + default=False) + + def execute(self, context): + count = 0 + min_co = Vector((0,0,0)) + max_co = Vector((0,0,0)) + for ob in context.selected_objects: + + if(ob == None or ob.type != 'MESH'): + continue + + context.scene.objects.active = ob + + # make each selected object a smoke flow + bpy.ops.object.modifier_add(type='SMOKE') + ob.modifiers[-1].smoke_type = 'FLOW' + + psys = ob.particle_systems[-1] + if self.style == 'PUFF': + psys.settings.frame_end = psys.settings.frame_start + psys.settings.emit_from = 'VOLUME' + psys.settings.distribution = 'RAND' + elif self.style == 'FIRE': + psys.settings.effector_weights.gravity = -1 + psys.settings.lifetime = 5 + psys.settings.count = 100000 + + ob.modifiers[-2].flow_settings.initial_velocity = True + ob.modifiers[-2].flow_settings.temperature = 2 + + psys.settings.use_render_emitter = self.show_flows + + # store bounding box min/max for the domain object + for i in range(0, 8): + bb_vec = Vector((ob.bound_box[i][0], ob.bound_box[i][1], ob.bound_box[i][2])) * ob.matrix_world + + if count == 0 and i == 0: + min_co += bb_vec + max_co += bb_vec + else: + min_co[0] = min(bb_vec[0], min_co[0]) + min_co[1] = min(bb_vec[1], min_co[1]) + min_co[2] = min(bb_vec[2], min_co[2]) + max_co[0] = max(bb_vec[0], max_co[0]) + max_co[1] = max(bb_vec[1], max_co[1]) + max_co[2] = max(bb_vec[2], max_co[2]) + + count += 1 + + if count == 0: + self.report({'ERROR'}, "Select at least one mesh object.") + return {'CANCELLED'} + + # add the smoke domain object + bpy.ops.mesh.primitive_cube_add() + ob = context.active_object + ob.name = "Smoke Domain" + + # give the smoke some room above the flows + ob.location[0] = (max_co[0] + min_co[0]) * 0.5 + ob.location[1] = (max_co[1] + min_co[1]) * 0.5 + ob.location[2] = max_co[2] - min_co[2] + ob.scale[0] = max_co[0] - min_co[0] + ob.scale[1] = max_co[1] - min_co[1] + ob.scale[2] = 2*(max_co[2] - min_co[2]) + + # setup smoke domain + bpy.ops.object.modifier_add(type='SMOKE') + ob.modifiers[-1].smoke_type = 'DOMAIN' + if self.style == 'FIRE': + ob.modifiers[-1].domain_settings.use_dissolve_smoke = True + ob.modifiers[-1].domain_settings.dissolve_speed = 20 + ob.modifiers[-1].domain_settings.use_high_resolution = True + + # create a volume material with a voxel data texture for the domain + bpy.ops.object.material_slot_add() + + mat = ob.material_slots[0].material + mat.name = "Smoke Domain Material" + mat.type = 'VOLUME' + mat.volume.density = 0 + mat.volume.density_scale = 5 + + mat.texture_slots.add() + mat.texture_slots[0].texture = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA') + mat.texture_slots[0].texture.voxel_data.domain_object = ob + mat.texture_slots[0].use_map_color_emission = False + mat.texture_slots[0].use_map_density = True + + # for fire add a second texture for emission and emission color + if self.style == 'FIRE': + mat.volume.emission = 5 + mat.texture_slots.add() + mat.texture_slots[1].texture = bpy.data.textures.new("Smoke Heat", 'VOXEL_DATA') + mat.texture_slots[1].texture.voxel_data.domain_object = ob + mat.texture_slots[1].texture.use_color_ramp = True + + ramp = mat.texture_slots[1].texture.color_ramp + + elem = ramp.elements.new(0.333) + elem.color[0] = elem.color[3] = 1 + elem.color[1] = elem.color[2] = 0 + + elem = ramp.elements.new(0.666) + elem.color[0] = elem.color[1] = elem.color[3] = 1 + elem.color[2] = 0 + + mat.texture_slots[1].use_map_emission = True + mat.texture_slots[1].blend_type = 'MULTIPLY' + + return {'FINISHED'} + +class MakeFluid(bpy.types.Operator): + bl_idname = "object.make_fluid" + bl_label = "Make Fluid" + bl_options = {'REGISTER', 'UNDO'} + + style = EnumProperty(items=( + ('INFLOW', "Inflow", ""), + ('BASIC', "Basic", "")), + name="Fluid Style", + description="", + default='BASIC') + + initial_velocity = FloatVectorProperty(name="Initial Velocity", + description="Initial velocity of the fluid", + default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='VELOCITY') + + show_flows = BoolProperty(name="Render Fluid Objects", + description="Keep the fluid objects visible during rendering.", + default=False) + + start_baking = BoolProperty(name="Start Fluid Bake", + description="Start baking the fluid immediately after creating the domain object.", + default=False) + + def execute(self, context): + count = 0 + min_co = Vector((0,0,0)) + max_co = Vector((0,0,0)) + for ob in context.selected_objects: + + if(ob == None or ob.type != 'MESH'): + continue + + context.scene.objects.active = ob + + # make each selected object a fluid + bpy.ops.object.modifier_add(type='FLUID_SIMULATION') + if self.style == 'INFLOW': + ob.modifiers[-1].settings.type = 'INFLOW' + ob.modifiers[-1].settings.inflow_velocity = self.initial_velocity.copy() + else: + ob.modifiers[-1].settings.type = 'FLUID' + ob.modifiers[-1].settings.initial_velocity = self.initial_velocity.copy() + + ob.hide_render = not self.show_flows + + # store bounding box min/max for the domain object + for i in range(0, 8): + bb_vec = Vector((ob.bound_box[i][0], ob.bound_box[i][1], ob.bound_box[i][2])) * ob.matrix_world + + if count == 0 and i == 0: + min_co += bb_vec + max_co += bb_vec + else: + min_co[0] = min(bb_vec[0], min_co[0]) + min_co[1] = min(bb_vec[1], min_co[1]) + min_co[2] = min(bb_vec[2], min_co[2]) + max_co[0] = max(bb_vec[0], max_co[0]) + max_co[1] = max(bb_vec[1], max_co[1]) + max_co[2] = max(bb_vec[2], max_co[2]) + + count += 1 + + if count == 0: + self.report({'ERROR'}, "Select at least one mesh object.") + return {'CANCELLED'} + + # add the fluid domain object + bpy.ops.mesh.primitive_cube_add() + ob = context.active_object + ob.name = "Fluid Domain" + + # give the smoke some room above the flows + ob.location[0] = (max_co[0] + min_co[0]) * 0.5 + ob.location[1] = (max_co[1] + min_co[1]) * 0.5 + ob.location[2] = min_co[2] - max_co[2] + ob.scale[0] = max_co[0] - min_co[0] + ob.scale[1] = max_co[1] - min_co[1] + ob.scale[2] = 2*(max_co[2] - min_co[2]) + + # setup smoke domain + bpy.ops.object.modifier_add(type='FLUID_SIMULATION') + ob.modifiers[-1].settings.type = 'DOMAIN' + + # make the domain smooth so it renders nicely + bpy.ops.object.shade_smooth() + + # create a ray-transparent material for the domain + bpy.ops.object.material_slot_add() + + mat = ob.material_slots[0].material + mat.name = "Fluid Domain Material" + mat.specular_intensity = 1 + mat.specular_hardness = 100 + mat.use_transparency = True + mat.alpha = 0 + mat.transparency_method = 'RAYTRACE' + mat.raytrace_transparency.ior = 1.33 + mat.raytrace_transparency.depth = 4 + + if self.start_baking: + bpy.ops.fluid.bake() + + return {'FINISHED'} \ No newline at end of file -- cgit v1.2.3