21640fa3ee6bef24823f2d0eb099b8ac672807c6
[blender-staging.git] / release / scripts / startup / bl_operators / object_quick_effects.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 from mathutils import Vector
22 import bpy
23 from bpy.props import BoolProperty, EnumProperty, IntProperty, FloatProperty, FloatVectorProperty
24
25
26 class MakeFur(bpy.types.Operator):
27     bl_idname = "object.make_fur"
28     bl_label = "Make Fur"
29     bl_options = {'REGISTER', 'UNDO'}
30
31     density = EnumProperty(items=(
32                         ('LIGHT', "Light", ""),
33                         ('MEDIUM', "Medium", ""),
34                         ('HEAVY', "Heavy", "")),
35                 name="Fur Density",
36                 description="",
37                 default='MEDIUM')
38
39     view_percentage = IntProperty(name="View %",
40             default=10, min=1, max=100, soft_min=1, soft_max=100)
41
42     length = FloatProperty(name="Length",
43             default=0.1, min=0.001, max=100, soft_min=0.01, soft_max=10)
44
45     def execute(self, context):
46         fake_context = bpy.context.copy()
47         mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH']
48
49         if not mesh_objects:
50             self.report({'ERROR'}, "Select at least one mesh object.")
51             return {'CANCELLED'}
52
53         mat = bpy.data.materials.new("Fur Material")
54         mat.strand.tip_size = 0.25
55         mat.strand.blend_distance = 0.5
56
57         for obj in mesh_objects:
58             fake_context["object"] = obj
59             bpy.ops.object.particle_system_add(fake_context)
60
61             psys = obj.particle_systems[-1]
62             psys.settings.type = 'HAIR'
63
64             if self.density == 'LIGHT':
65                 psys.settings.count = 100
66             elif self.density == 'MEDIUM':
67                 psys.settings.count = 1000
68             elif self.density == 'HEAVY':
69                 psys.settings.count = 10000
70
71             psys.settings.child_nbr = self.view_percentage
72             psys.settings.hair_length = self.length
73             psys.settings.use_strand_primitive = True
74             psys.settings.use_hair_bspline = True
75             psys.settings.child_type = 'INTERPOLATED'
76
77             obj.data.materials.append(mat)
78             obj.particle_systems[-1].settings.material = len(obj.data.materials)
79
80         return {'FINISHED'}
81
82
83 def obj_bb_minmax(obj, min_co, max_co):
84     for i in range(0, 8):
85         bb_vec = Vector((obj.bound_box[i][0], obj.bound_box[i][1], obj.bound_box[i][2])) * obj.matrix_world
86
87         min_co[0] = min(bb_vec[0], min_co[0])
88         min_co[1] = min(bb_vec[1], min_co[1])
89         min_co[2] = min(bb_vec[2], min_co[2])
90         max_co[0] = max(bb_vec[0], max_co[0])
91         max_co[1] = max(bb_vec[1], max_co[1])
92         max_co[2] = max(bb_vec[2], max_co[2])
93
94
95 class MakeSmoke(bpy.types.Operator):
96     bl_idname = "object.make_smoke"
97     bl_label = "Make Smoke"
98     bl_options = {'REGISTER', 'UNDO'}
99
100     style = EnumProperty(items=(
101                         ('STREAM', "Stream", ""),
102                         ('PUFF', "Puff", ""),
103                         ('FIRE', "Fire", "")),
104                 name="Smoke Style",
105                 description="",
106                 default='STREAM')
107
108     show_flows = BoolProperty(name="Render Smoke Objects",
109                 description="Keep the smoke objects visible during rendering.",
110                 default=False)
111
112     def execute(self, context):
113         fake_context = bpy.context.copy()
114         mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH']
115         min_co = Vector((100000, 100000, 100000))
116         max_co = Vector((-100000, -100000, -100000))
117
118         if not mesh_objects:
119             self.report({'ERROR'}, "Select at least one mesh object.")
120             return {'CANCELLED'}
121
122         for obj in mesh_objects:
123             fake_context["object"] = obj
124             # make each selected object a smoke flow
125             bpy.ops.object.modifier_add(fake_context, type='SMOKE')
126             obj.modifiers[-1].smoke_type = 'FLOW'
127
128             psys = obj.particle_systems[-1]
129             if self.style == 'PUFF':
130                 psys.settings.frame_end = psys.settings.frame_start
131                 psys.settings.emit_from = 'VOLUME'
132                 psys.settings.distribution = 'RAND'
133             elif self.style == 'FIRE':
134                 psys.settings.effector_weights.gravity = -1
135                 psys.settings.lifetime = 5
136                 psys.settings.count = 100000
137
138                 obj.modifiers[-2].flow_settings.initial_velocity = True
139                 obj.modifiers[-2].flow_settings.temperature = 2
140
141             psys.settings.use_render_emitter = self.show_flows
142             if not self.show_flows:
143                 obj.draw_type = 'WIRE'
144
145             # store bounding box min/max for the domain object
146             obj_bb_minmax(obj, min_co, max_co)
147
148         # add the smoke domain object
149         bpy.ops.mesh.primitive_cube_add()
150         obj = context.active_object
151         obj.name = "Smoke Domain"
152
153         # give the smoke some room above the flows
154         obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, 1.0))
155         obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0))
156
157         # setup smoke domain
158         bpy.ops.object.modifier_add(type='SMOKE')
159         obj.modifiers[-1].smoke_type = 'DOMAIN'
160         if self.style == 'FIRE':
161             obj.modifiers[-1].domain_settings.use_dissolve_smoke = True
162             obj.modifiers[-1].domain_settings.dissolve_speed = 20
163             obj.modifiers[-1].domain_settings.use_high_resolution = True
164
165         # create a volume material with a voxel data texture for the domain
166         bpy.ops.object.material_slot_add()
167
168         mat = bpy.data.materials.new("Smoke Domain Material")
169         obj.material_slots[0].material = mat
170         mat.type = 'VOLUME'
171         mat.volume.density = 0
172         mat.volume.density_scale = 5
173
174         mat.texture_slots.add()
175         mat.texture_slots[0].texture = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA')
176         mat.texture_slots[0].texture.voxel_data.domain_object = obj
177         mat.texture_slots[0].use_map_color_emission = False
178         mat.texture_slots[0].use_map_density = True
179
180         # for fire add a second texture for emission and emission color
181         if self.style == 'FIRE':
182             mat.volume.emission = 5
183             mat.texture_slots.add()
184             mat.texture_slots[1].texture = bpy.data.textures.new("Smoke Heat", 'VOXEL_DATA')
185             mat.texture_slots[1].texture.voxel_data.domain_object = obj
186             mat.texture_slots[1].texture.use_color_ramp = True
187
188             ramp = mat.texture_slots[1].texture.color_ramp
189
190             elem = ramp.elements.new(0.333)
191             elem.color[0] = elem.color[3] = 1
192             elem.color[1] = elem.color[2] = 0
193
194             elem = ramp.elements.new(0.666)
195             elem.color[0] = elem.color[1] = elem.color[3] = 1
196             elem.color[2] = 0
197
198             mat.texture_slots[1].use_map_emission = True
199             mat.texture_slots[1].blend_type = 'MULTIPLY'
200
201         return {'FINISHED'}
202
203
204 class MakeFluid(bpy.types.Operator):
205     bl_idname = "object.make_fluid"
206     bl_label = "Make Fluid"
207     bl_options = {'REGISTER', 'UNDO'}
208
209     style = EnumProperty(items=(
210                         ('INFLOW', "Inflow", ""),
211                         ('BASIC', "Basic", "")),
212                 name="Fluid Style",
213                 description="",
214                 default='BASIC')
215
216     initial_velocity = FloatVectorProperty(name="Initial Velocity",
217         description="Initial velocity of the fluid",
218         default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='VELOCITY')
219
220     show_flows = BoolProperty(name="Render Fluid Objects",
221                 description="Keep the fluid objects visible during rendering.",
222                 default=False)
223
224     start_baking = BoolProperty(name="Start Fluid Bake",
225                 description="Start baking the fluid immediately after creating the domain object.",
226                 default=False)
227
228     def execute(self, context):
229         fake_context = bpy.context.copy()
230         mesh_objects = [obj for obj in context.selected_objects if (obj.type == 'MESH' and not 0 in obj.dimensions)]
231         min_co = Vector((100000, 100000, 100000))
232         max_co = Vector((-100000, -100000, -100000))
233
234         if not mesh_objects:
235             self.report({'ERROR'}, "Select at least one mesh object.")
236             return {'CANCELLED'}
237
238         for obj in mesh_objects:
239             fake_context["object"] = obj
240             # make each selected object a fluid
241             bpy.ops.object.modifier_add(fake_context, type='FLUID_SIMULATION')
242
243             # fluid has to be before constructive modifiers, so it might not be the last modifier
244             for mod in obj.modifiers:
245                 if mod.type == 'FLUID_SIMULATION':
246                     break
247
248             if self.style == 'INFLOW':
249                 mod.settings.type = 'INFLOW'
250                 mod.settings.inflow_velocity = self.initial_velocity.copy()
251             else:
252                 mod.settings.type = 'FLUID'
253                 mod.settings.initial_velocity = self.initial_velocity.copy()
254
255             obj.hide_render = not self.show_flows
256             if not self.show_flows:
257                 obj.draw_type = 'WIRE'
258
259             # store bounding box min/max for the domain object
260             obj_bb_minmax(obj, min_co, max_co)
261
262         # add the fluid domain object
263         bpy.ops.mesh.primitive_cube_add()
264         obj = context.active_object
265         obj.name = "Fluid Domain"
266
267         # give the fluid some room below the flows and scale with initial velocity
268         v = 0.5 * self.initial_velocity
269         obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v
270         obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0)) + Vector((abs(v[0]), abs(v[1]), abs(v[2])))
271
272         # setup smoke domain
273         bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
274         obj.modifiers[-1].settings.type = 'DOMAIN'
275
276         # make the domain smooth so it renders nicely
277         bpy.ops.object.shade_smooth()
278
279         # create a ray-transparent material for the domain
280         bpy.ops.object.material_slot_add()
281
282         mat = bpy.data.materials.new("Fluid Domain Material")
283         obj.material_slots[0].material = mat
284
285         mat.specular_intensity = 1
286         mat.specular_hardness = 100
287         mat.use_transparency = True
288         mat.alpha = 0.0
289         mat.transparency_method = 'RAYTRACE'
290         mat.raytrace_transparency.ior = 1.33
291         mat.raytrace_transparency.depth = 4
292
293         if self.start_baking:
294             bpy.ops.fluid.bake()
295
296         return {'FINISHED'}