2011-03-30 10:29:32 +00:00
|
|
|
# ##### 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 #####
|
|
|
|
|
2011-07-25 05:54:32 +00:00
|
|
|
# <pep8-80 compliant>
|
2011-03-30 10:29:32 +00:00
|
|
|
|
|
|
|
from mathutils import Vector
|
|
|
|
import bpy
|
2011-08-12 06:57:00 +00:00
|
|
|
from bpy.types import Operator
|
2011-07-25 05:54:32 +00:00
|
|
|
from bpy.props import (BoolProperty,
|
|
|
|
EnumProperty,
|
|
|
|
IntProperty,
|
|
|
|
FloatProperty,
|
|
|
|
FloatVectorProperty,
|
|
|
|
)
|
2011-03-30 10:29:32 +00:00
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
|
|
|
|
def object_ensure_material(obj, mat_name):
|
|
|
|
""" Use an existing material or add a new one.
|
|
|
|
"""
|
|
|
|
mat = mat_slot = None
|
|
|
|
for mat_slot in obj.material_slots:
|
|
|
|
mat = mat_slot.material
|
|
|
|
if mat:
|
|
|
|
break
|
|
|
|
if mat is None:
|
|
|
|
mat = bpy.data.materials.new(mat_name)
|
|
|
|
if mat_slot:
|
|
|
|
mat_slot.material = mat
|
|
|
|
else:
|
|
|
|
obj.data.materials.append(mat)
|
|
|
|
return mat
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class QuickFur(Operator):
|
2011-06-12 11:14:28 +00:00
|
|
|
bl_idname = "object.quick_fur"
|
|
|
|
bl_label = "Quick Fur"
|
2011-03-30 10:29:32 +00:00
|
|
|
bl_options = {'REGISTER', 'UNDO'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-08-19 19:25:20 +00:00
|
|
|
density = EnumProperty(
|
|
|
|
name="Fur Density",
|
|
|
|
items=(('LIGHT', "Light", ""),
|
|
|
|
('MEDIUM', "Medium", ""),
|
|
|
|
('HEAVY', "Heavy", "")),
|
|
|
|
default='MEDIUM',
|
|
|
|
)
|
|
|
|
view_percentage = IntProperty(
|
|
|
|
name="View %",
|
|
|
|
min=1, max=100,
|
|
|
|
soft_min=1, soft_max=100,
|
|
|
|
default=10,
|
|
|
|
)
|
|
|
|
length = FloatProperty(
|
|
|
|
name="Length",
|
|
|
|
min=0.001, max=100,
|
|
|
|
soft_min=0.01, soft_max=10,
|
|
|
|
default=0.1,
|
|
|
|
)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
def execute(self, context):
|
2013-01-23 07:52:31 +00:00
|
|
|
fake_context = context.copy()
|
2011-07-25 05:54:32 +00:00
|
|
|
mesh_objects = [obj for obj in context.selected_objects
|
|
|
|
if obj.type == 'MESH']
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
if not mesh_objects:
|
2011-09-19 14:00:42 +00:00
|
|
|
self.report({'ERROR'}, "Select at least one mesh object")
|
2011-03-31 11:49:01 +00:00
|
|
|
return {'CANCELLED'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
mat = bpy.data.materials.new("Fur Material")
|
|
|
|
mat.strand.tip_size = 0.25
|
|
|
|
mat.strand.blend_distance = 0.5
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
for obj in mesh_objects:
|
2011-04-20 17:51:56 +00:00
|
|
|
fake_context["object"] = obj
|
2011-03-31 11:49:01 +00:00
|
|
|
bpy.ops.object.particle_system_add(fake_context)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
psys = obj.particle_systems[-1]
|
2011-03-30 10:29:32 +00:00
|
|
|
psys.settings.type = 'HAIR'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
if self.density == 'LIGHT':
|
|
|
|
psys.settings.count = 100
|
|
|
|
elif self.density == 'MEDIUM':
|
|
|
|
psys.settings.count = 1000
|
|
|
|
elif self.density == 'HEAVY':
|
|
|
|
psys.settings.count = 10000
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
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'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.data.materials.append(mat)
|
2011-11-08 01:32:34 +00:00
|
|
|
psys.settings.material = len(obj.data.materials)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class QuickExplode(Operator):
|
2011-06-12 11:14:28 +00:00
|
|
|
bl_idname = "object.quick_explode"
|
|
|
|
bl_label = "Quick Explode"
|
|
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
|
2011-08-19 19:25:20 +00:00
|
|
|
style = EnumProperty(
|
|
|
|
name="Explode Style",
|
|
|
|
items=(('EXPLODE', "Explode", ""),
|
|
|
|
('BLEND', "Blend", "")),
|
|
|
|
default='EXPLODE',
|
|
|
|
)
|
|
|
|
amount = IntProperty(
|
|
|
|
name="Amount of pieces",
|
|
|
|
min=2, max=10000,
|
|
|
|
soft_min=2, soft_max=10000,
|
|
|
|
default=100,
|
|
|
|
)
|
|
|
|
frame_duration = IntProperty(
|
|
|
|
name="Duration",
|
|
|
|
min=1, max=300000,
|
|
|
|
soft_min=1, soft_max=10000,
|
|
|
|
default=50,
|
|
|
|
)
|
2011-06-12 11:14:28 +00:00
|
|
|
|
2011-08-19 19:25:20 +00:00
|
|
|
frame_start = IntProperty(
|
|
|
|
name="Start Frame",
|
|
|
|
min=1, max=300000,
|
|
|
|
soft_min=1, soft_max=10000,
|
|
|
|
default=1,
|
|
|
|
)
|
|
|
|
frame_end = IntProperty(
|
|
|
|
name="End Frame",
|
|
|
|
min=1, max=300000,
|
|
|
|
soft_min=1, soft_max=10000,
|
|
|
|
default=10,
|
|
|
|
)
|
2011-06-12 11:14:28 +00:00
|
|
|
|
2011-08-19 19:25:20 +00:00
|
|
|
velocity = FloatProperty(
|
|
|
|
name="Outwards Velocity",
|
|
|
|
min=0, max=300000,
|
|
|
|
soft_min=0, soft_max=10,
|
|
|
|
default=1,
|
|
|
|
)
|
2011-06-12 11:14:28 +00:00
|
|
|
|
2011-08-19 19:25:20 +00:00
|
|
|
fade = BoolProperty(
|
|
|
|
name="Fade",
|
2011-09-19 14:00:42 +00:00
|
|
|
description="Fade the pieces over time",
|
2011-08-19 19:25:20 +00:00
|
|
|
default=True,
|
|
|
|
)
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2013-01-23 07:52:31 +00:00
|
|
|
fake_context = context.copy()
|
2011-06-15 00:16:30 +00:00
|
|
|
obj_act = context.active_object
|
|
|
|
|
2011-07-01 13:26:20 +00:00
|
|
|
if obj_act is None or obj_act.type != 'MESH':
|
2011-06-15 00:16:30 +00:00
|
|
|
self.report({'ERROR'}, "Active object is not a mesh")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
mesh_objects = [obj for obj in context.selected_objects
|
|
|
|
if obj.type == 'MESH' and obj != obj_act]
|
|
|
|
mesh_objects.insert(0, obj_act)
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
if self.style == 'BLEND' and len(mesh_objects) != 2:
|
2011-06-15 00:16:30 +00:00
|
|
|
self.report({'ERROR'}, "Select two mesh objects")
|
2012-06-19 16:45:48 +00:00
|
|
|
self.style = 'EXPLODE'
|
2011-06-12 11:14:28 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
elif not mesh_objects:
|
2011-06-15 00:16:30 +00:00
|
|
|
self.report({'ERROR'}, "Select at least one mesh object")
|
2011-06-12 11:14:28 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
for obj in mesh_objects:
|
2011-06-15 00:16:30 +00:00
|
|
|
if obj.particle_systems:
|
2011-07-25 05:54:32 +00:00
|
|
|
self.report({'ERROR'},
|
|
|
|
"Object %r already has a "
|
|
|
|
"particle system" % obj.name)
|
|
|
|
|
2011-06-12 11:14:28 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
if self.fade:
|
|
|
|
tex = bpy.data.textures.new("Explode fade", 'BLEND')
|
|
|
|
tex.use_color_ramp = True
|
|
|
|
|
|
|
|
if self.style == 'BLEND':
|
|
|
|
tex.color_ramp.elements[0].position = 0.333
|
|
|
|
tex.color_ramp.elements[1].position = 0.666
|
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
tex.color_ramp.elements[0].color[3] = 1.0
|
|
|
|
tex.color_ramp.elements[1].color[3] = 0.0
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
if self.style == 'BLEND':
|
2011-06-15 00:16:30 +00:00
|
|
|
from_obj = mesh_objects[1]
|
|
|
|
to_obj = mesh_objects[0]
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
for obj in mesh_objects:
|
|
|
|
fake_context["object"] = obj
|
|
|
|
bpy.ops.object.particle_system_add(fake_context)
|
|
|
|
|
|
|
|
settings = obj.particle_systems[-1].settings
|
|
|
|
settings.count = self.amount
|
2011-06-15 00:16:30 +00:00
|
|
|
settings.frame_start = self.frame_start
|
|
|
|
settings.frame_end = self.frame_end - self.frame_duration
|
|
|
|
settings.lifetime = self.frame_duration
|
2011-06-12 11:14:28 +00:00
|
|
|
settings.normal_factor = self.velocity
|
|
|
|
settings.render_type = 'NONE'
|
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
explode = obj.modifiers.new(name='Explode', type='EXPLODE')
|
2011-06-12 11:14:28 +00:00
|
|
|
explode.use_edge_cut = True
|
|
|
|
|
|
|
|
if self.fade:
|
|
|
|
explode.show_dead = False
|
2011-07-25 05:54:32 +00:00
|
|
|
uv = obj.data.uv_textures.new("Explode fade")
|
2011-06-12 11:14:28 +00:00
|
|
|
explode.particle_uv = uv.name
|
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
mat = object_ensure_material(obj, "Explode Fade")
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
mat.use_transparency = True
|
|
|
|
mat.use_transparent_shadows = True
|
2011-06-15 00:16:30 +00:00
|
|
|
mat.alpha = 0.0
|
|
|
|
mat.specular_alpha = 0.0
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
tex_slot = mat.texture_slots.add()
|
|
|
|
|
|
|
|
tex_slot.texture = tex
|
|
|
|
tex_slot.texture_coords = 'UV'
|
|
|
|
tex_slot.uv_layer = uv.name
|
|
|
|
|
|
|
|
tex_slot.use_map_alpha = True
|
|
|
|
|
|
|
|
if self.style == 'BLEND':
|
|
|
|
if obj == to_obj:
|
2011-06-15 00:16:30 +00:00
|
|
|
tex_slot.alpha_factor = -1.0
|
2011-06-12 11:14:28 +00:00
|
|
|
elem = tex.color_ramp.elements[1]
|
|
|
|
else:
|
|
|
|
elem = tex.color_ramp.elements[0]
|
2012-06-19 16:45:48 +00:00
|
|
|
# Keep already defined alpha!
|
|
|
|
elem.color[:3] = mat.diffuse_color
|
2011-06-12 11:14:28 +00:00
|
|
|
else:
|
|
|
|
tex_slot.use_map_color_diffuse = False
|
|
|
|
|
|
|
|
if self.style == 'BLEND':
|
|
|
|
settings.physics_type = 'KEYED'
|
|
|
|
settings.use_emit_random = False
|
|
|
|
settings.rotation_mode = 'NOR'
|
|
|
|
|
|
|
|
psys = obj.particle_systems[-1]
|
|
|
|
|
|
|
|
fake_context["particle_system"] = obj.particle_systems[-1]
|
|
|
|
bpy.ops.particle.new_target(fake_context)
|
|
|
|
bpy.ops.particle.new_target(fake_context)
|
|
|
|
|
|
|
|
if obj == from_obj:
|
|
|
|
psys.targets[1].object = to_obj
|
|
|
|
else:
|
|
|
|
psys.targets[0].object = from_obj
|
|
|
|
settings.normal_factor = -self.velocity
|
|
|
|
explode.show_unborn = False
|
|
|
|
explode.show_dead = True
|
|
|
|
else:
|
|
|
|
settings.factor_random = self.velocity
|
2011-06-15 00:16:30 +00:00
|
|
|
settings.angular_velocity_factor = self.velocity / 10.0
|
2011-06-12 11:14:28 +00:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
def invoke(self, context, event):
|
|
|
|
self.frame_start = context.scene.frame_current
|
|
|
|
self.frame_end = self.frame_start + self.frame_duration
|
|
|
|
return self.execute(context)
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-06-21 17:17:51 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
def obj_bb_minmax(obj, min_co, max_co):
|
|
|
|
for i in range(0, 8):
|
2011-07-25 05:54:32 +00:00
|
|
|
bb_vec = obj.matrix_world * Vector(obj.bound_box[i])
|
2011-03-31 11:49:01 +00:00
|
|
|
|
|
|
|
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])
|
2014-09-17 08:36:17 +00:00
|
|
|
|
|
|
|
|
2014-09-07 03:15:26 +00:00
|
|
|
def grid_location(x, y):
|
|
|
|
return (x * 200, y * 150)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class QuickSmoke(Operator):
|
2011-06-12 11:14:28 +00:00
|
|
|
bl_idname = "object.quick_smoke"
|
|
|
|
bl_label = "Quick Smoke"
|
2011-03-30 10:29:32 +00:00
|
|
|
bl_options = {'REGISTER', 'UNDO'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-07-25 05:54:32 +00:00
|
|
|
style = EnumProperty(
|
2011-08-19 19:25:20 +00:00
|
|
|
name="Smoke Style",
|
2014-09-07 15:01:24 +00:00
|
|
|
items=(('SMOKE', "Smoke", ""),
|
2011-07-25 05:54:32 +00:00
|
|
|
('FIRE', "Fire", ""),
|
2014-09-07 15:01:24 +00:00
|
|
|
('BOTH', "Smoke + Fire", ""),
|
2011-07-25 05:54:32 +00:00
|
|
|
),
|
2014-09-07 15:01:24 +00:00
|
|
|
default='SMOKE',
|
2011-07-25 05:54:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
show_flows = BoolProperty(
|
|
|
|
name="Render Smoke Objects",
|
2011-09-19 14:00:42 +00:00
|
|
|
description="Keep the smoke objects visible during rendering",
|
2011-07-25 05:54:32 +00:00
|
|
|
default=False,
|
|
|
|
)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
def execute(self, context):
|
2013-01-23 07:52:31 +00:00
|
|
|
fake_context = context.copy()
|
2011-07-25 05:54:32 +00:00
|
|
|
mesh_objects = [obj for obj in context.selected_objects
|
|
|
|
if obj.type == 'MESH']
|
2011-06-15 00:16:30 +00:00
|
|
|
min_co = Vector((100000.0, 100000.0, 100000.0))
|
|
|
|
max_co = -min_co
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
if not mesh_objects:
|
2011-09-19 14:00:42 +00:00
|
|
|
self.report({'ERROR'}, "Select at least one mesh object")
|
2011-03-31 11:49:01 +00:00
|
|
|
return {'CANCELLED'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
for obj in mesh_objects:
|
2011-04-20 17:51:56 +00:00
|
|
|
fake_context["object"] = obj
|
2011-03-30 10:29:32 +00:00
|
|
|
# make each selected object a smoke flow
|
2011-04-20 17:51:56 +00:00
|
|
|
bpy.ops.object.modifier_add(fake_context, type='SMOKE')
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.modifiers[-1].smoke_type = 'FLOW'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2014-09-07 15:01:24 +00:00
|
|
|
# set type
|
|
|
|
obj.modifiers[-1].flow_settings.smoke_flow_type = self.style
|
2012-10-10 13:18:07 +00:00
|
|
|
|
2011-03-30 13:35:54 +00:00
|
|
|
if not self.show_flows:
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.draw_type = 'WIRE'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# store bounding box min/max for the domain object
|
2011-03-31 11:49:01 +00:00
|
|
|
obj_bb_minmax(obj, min_co, max_co)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# add the smoke domain object
|
|
|
|
bpy.ops.mesh.primitive_cube_add()
|
2011-03-31 11:49:01 +00:00
|
|
|
obj = context.active_object
|
|
|
|
obj.name = "Smoke Domain"
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# give the smoke some room above the flows
|
2011-04-01 02:41:15 +00:00
|
|
|
obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, 1.0))
|
|
|
|
obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0))
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# setup smoke domain
|
2011-04-20 17:51:56 +00:00
|
|
|
bpy.ops.object.modifier_add(type='SMOKE')
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.modifiers[-1].smoke_type = 'DOMAIN'
|
2014-09-07 15:01:24 +00:00
|
|
|
if self.style == 'FIRE' or self.style == 'BOTH':
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.modifiers[-1].domain_settings.use_high_resolution = True
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2014-09-07 03:15:26 +00:00
|
|
|
# Setup material
|
|
|
|
|
|
|
|
# Cycles
|
|
|
|
if context.scene.render.use_shading_nodes:
|
|
|
|
bpy.ops.object.material_slot_add()
|
|
|
|
|
|
|
|
mat = bpy.data.materials.new("Smoke Domain Material")
|
|
|
|
obj.material_slots[0].material = mat
|
|
|
|
|
|
|
|
# Make sure we use nodes
|
|
|
|
mat.use_nodes = True
|
|
|
|
|
|
|
|
# Set node variables and clear the default nodes
|
|
|
|
tree = mat.node_tree
|
|
|
|
nodes = tree.nodes
|
|
|
|
links = tree.links
|
|
|
|
|
|
|
|
nodes.clear()
|
|
|
|
|
|
|
|
# Create shader nodes
|
|
|
|
|
|
|
|
# Material output
|
|
|
|
node_out = nodes.new(type='ShaderNodeOutputMaterial')
|
|
|
|
node_out.location = grid_location(6, 1)
|
|
|
|
|
|
|
|
# Add shader 1
|
|
|
|
node_add_shader_1 = nodes.new(type='ShaderNodeAddShader')
|
|
|
|
node_add_shader_1.location = grid_location(5, 1)
|
|
|
|
links.new(node_add_shader_1.outputs["Shader"],
|
|
|
|
node_out.inputs["Volume"])
|
|
|
|
|
2014-09-25 10:44:06 +00:00
|
|
|
if self.style in {'SMOKE', 'BOTH'}:
|
|
|
|
# Smoke
|
|
|
|
|
|
|
|
# Add shader 2
|
|
|
|
node_add_shader_2 = nodes.new(type='ShaderNodeAddShader')
|
|
|
|
node_add_shader_2.location = grid_location(4, 2)
|
|
|
|
links.new(node_add_shader_2.outputs["Shader"],
|
|
|
|
node_add_shader_1.inputs[0])
|
|
|
|
|
|
|
|
# Volume scatter
|
|
|
|
node_scatter = nodes.new(type='ShaderNodeVolumeScatter')
|
|
|
|
node_scatter.location = grid_location(3, 3)
|
|
|
|
links.new(node_scatter.outputs["Volume"],
|
|
|
|
node_add_shader_2.inputs[0])
|
|
|
|
|
|
|
|
# Volume absorption
|
|
|
|
node_absorption = nodes.new(type='ShaderNodeVolumeAbsorption')
|
|
|
|
node_absorption.location = grid_location(3, 2)
|
|
|
|
links.new(node_absorption.outputs["Volume"],
|
|
|
|
node_add_shader_2.inputs[1])
|
|
|
|
|
|
|
|
# Density Multiplier
|
|
|
|
node_densmult = nodes.new(type='ShaderNodeMath')
|
|
|
|
node_densmult.location = grid_location(2, 2)
|
|
|
|
node_densmult.operation = 'MULTIPLY'
|
|
|
|
node_densmult.inputs[1].default_value = 5.0
|
|
|
|
links.new(node_densmult.outputs["Value"],
|
|
|
|
node_scatter.inputs["Density"])
|
|
|
|
links.new(node_densmult.outputs["Value"],
|
|
|
|
node_absorption.inputs["Density"])
|
|
|
|
|
|
|
|
# Attribute "density"
|
|
|
|
node_attrib_density = nodes.new(type='ShaderNodeAttribute')
|
|
|
|
node_attrib_density.attribute_name = "density"
|
|
|
|
node_attrib_density.location = grid_location(1, 2)
|
|
|
|
links.new(node_attrib_density.outputs["Fac"],
|
|
|
|
node_densmult.inputs[0])
|
|
|
|
|
|
|
|
# Attribute "color"
|
|
|
|
node_attrib_color = nodes.new(type='ShaderNodeAttribute')
|
|
|
|
node_attrib_color.attribute_name = "color"
|
|
|
|
node_attrib_color.location = grid_location(2, 3)
|
|
|
|
links.new(node_attrib_color.outputs["Color"],
|
|
|
|
node_scatter.inputs["Color"])
|
|
|
|
links.new(node_attrib_color.outputs["Color"],
|
|
|
|
node_absorption.inputs["Color"])
|
|
|
|
|
|
|
|
if self.style in {'FIRE', 'BOTH'}:
|
|
|
|
# Fire
|
|
|
|
|
|
|
|
# Emission
|
|
|
|
node_emission = nodes.new(type='ShaderNodeEmission')
|
|
|
|
node_emission.inputs["Color"].default_value = (0.8, 0.1, 0.01, 1.0)
|
|
|
|
node_emission.location = grid_location(4, 1)
|
|
|
|
links.new(node_emission.outputs["Emission"],
|
|
|
|
node_add_shader_1.inputs[1])
|
|
|
|
|
|
|
|
# Flame strength multiplier
|
|
|
|
node_flame_strength_mult = nodes.new(type='ShaderNodeMath')
|
|
|
|
node_flame_strength_mult.location = grid_location(3, 1)
|
|
|
|
node_flame_strength_mult.operation = 'MULTIPLY'
|
|
|
|
node_flame_strength_mult.inputs[1].default_value = 2.5
|
|
|
|
links.new(node_flame_strength_mult.outputs["Value"],
|
|
|
|
node_emission.inputs["Strength"])
|
|
|
|
|
|
|
|
# Color ramp Flame
|
|
|
|
node_flame_ramp = nodes.new(type='ShaderNodeValToRGB')
|
|
|
|
node_flame_ramp.location = grid_location(1, 1)
|
|
|
|
ramp = node_flame_ramp.color_ramp
|
|
|
|
ramp.interpolation = 'EASE'
|
|
|
|
|
|
|
|
# orange
|
|
|
|
elem = ramp.elements.new(0.5)
|
|
|
|
elem.color = (1.0, 0.128, 0.0, 1.0)
|
|
|
|
|
|
|
|
# yellow
|
|
|
|
elem = ramp.elements.new(0.9)
|
|
|
|
elem.color = (0.9, 0.6, 0.1, 1.0)
|
|
|
|
|
|
|
|
links.new(node_flame_ramp.outputs["Color"],
|
|
|
|
node_emission.inputs["Color"])
|
|
|
|
|
|
|
|
# Attribute "flame"
|
|
|
|
node_attrib_flame = nodes.new(type='ShaderNodeAttribute')
|
|
|
|
node_attrib_flame.attribute_name = "flame"
|
|
|
|
node_attrib_flame.location = grid_location(0, 1)
|
|
|
|
links.new(node_attrib_flame.outputs["Fac"],
|
|
|
|
node_flame_ramp.inputs["Fac"])
|
|
|
|
links.new(node_attrib_flame.outputs["Fac"],
|
|
|
|
node_flame_strength_mult.inputs[0])
|
2014-09-07 03:15:26 +00:00
|
|
|
|
|
|
|
# Blender Internal
|
|
|
|
else:
|
|
|
|
# create a volume material with a voxel data texture for the domain
|
|
|
|
bpy.ops.object.material_slot_add()
|
|
|
|
|
|
|
|
mat = bpy.data.materials.new("Smoke Domain Material")
|
|
|
|
obj.material_slots[0].material = mat
|
|
|
|
mat.type = 'VOLUME'
|
|
|
|
mat.volume.density = 0
|
|
|
|
mat.volume.density_scale = 5
|
|
|
|
mat.volume.step_size = 0.1
|
|
|
|
|
|
|
|
tex = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA')
|
|
|
|
tex.voxel_data.domain_object = obj
|
|
|
|
tex.voxel_data.interpolation = 'TRICUBIC_BSPLINE'
|
|
|
|
|
|
|
|
tex_slot = mat.texture_slots.add()
|
|
|
|
tex_slot.texture = tex
|
|
|
|
tex_slot.texture_coords = 'ORCO'
|
|
|
|
tex_slot.use_map_color_emission = False
|
|
|
|
tex_slot.use_map_density = True
|
|
|
|
tex_slot.use_map_color_reflection = True
|
|
|
|
|
|
|
|
# for fire add a second texture for flame emission
|
|
|
|
mat.volume.emission_color = Vector((0.0, 0.0, 0.0))
|
|
|
|
tex = bpy.data.textures.new("Flame", 'VOXEL_DATA')
|
|
|
|
tex.voxel_data.domain_object = obj
|
|
|
|
tex.voxel_data.smoke_data_type = 'SMOKEFLAME'
|
|
|
|
tex.voxel_data.interpolation = 'TRICUBIC_BSPLINE'
|
|
|
|
tex.use_color_ramp = True
|
|
|
|
|
|
|
|
tex_slot = mat.texture_slots.add()
|
|
|
|
tex_slot.texture = tex
|
|
|
|
tex_slot.texture_coords = 'ORCO'
|
|
|
|
|
|
|
|
# add color ramp for flame color
|
|
|
|
ramp = tex.color_ramp
|
|
|
|
# dark orange
|
|
|
|
elem = ramp.elements.new(0.333)
|
2014-09-07 15:01:24 +00:00
|
|
|
elem.color = (0.2, 0.03, 0.0, 1.0)
|
|
|
|
|
2014-09-07 03:15:26 +00:00
|
|
|
# yellow glow
|
|
|
|
elem = ramp.elements.new(0.666)
|
2014-09-07 15:01:24 +00:00
|
|
|
elem.color = (1, 0.65, 0.25, 1.0)
|
2014-09-07 03:15:26 +00:00
|
|
|
|
|
|
|
mat.texture_slots[1].use_map_density = True
|
|
|
|
mat.texture_slots[1].use_map_emission = True
|
|
|
|
mat.texture_slots[1].emission_factor = 5
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
return {'FINISHED'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class QuickFluid(Operator):
|
2011-06-12 11:14:28 +00:00
|
|
|
bl_idname = "object.quick_fluid"
|
|
|
|
bl_label = "Quick Fluid"
|
2011-03-30 10:29:32 +00:00
|
|
|
bl_options = {'REGISTER', 'UNDO'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-07-25 05:54:32 +00:00
|
|
|
style = EnumProperty(
|
2011-08-19 19:25:20 +00:00
|
|
|
name="Fluid Style",
|
2011-07-25 05:54:32 +00:00
|
|
|
items=(('INFLOW', "Inflow", ""),
|
2011-08-19 19:25:20 +00:00
|
|
|
('BASIC', "Basic", "")),
|
|
|
|
default='BASIC',
|
|
|
|
)
|
2011-07-25 05:54:32 +00:00
|
|
|
initial_velocity = FloatVectorProperty(
|
|
|
|
name="Initial Velocity",
|
|
|
|
description="Initial velocity of the fluid",
|
2011-08-19 19:25:20 +00:00
|
|
|
min=-100.0, max=100.0,
|
2011-07-25 05:54:32 +00:00
|
|
|
default=(0.0, 0.0, 0.0),
|
|
|
|
subtype='VELOCITY',
|
|
|
|
)
|
|
|
|
show_flows = BoolProperty(
|
|
|
|
name="Render Fluid Objects",
|
2011-09-19 14:00:42 +00:00
|
|
|
description="Keep the fluid objects visible during rendering",
|
2011-07-25 05:54:32 +00:00
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
start_baking = BoolProperty(
|
|
|
|
name="Start Fluid Bake",
|
|
|
|
description=("Start baking the fluid immediately "
|
|
|
|
"after creating the domain object"),
|
|
|
|
default=False,
|
|
|
|
)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
def execute(self, context):
|
2013-01-23 07:52:31 +00:00
|
|
|
fake_context = context.copy()
|
2011-07-25 05:54:32 +00:00
|
|
|
mesh_objects = [obj for obj in context.selected_objects
|
2014-04-02 22:24:09 +00:00
|
|
|
if (obj.type == 'MESH' and 0.0 not in obj.dimensions)]
|
2013-04-20 13:23:53 +00:00
|
|
|
min_co = Vector((100000.0, 100000.0, 100000.0))
|
|
|
|
max_co = -min_co
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
if not mesh_objects:
|
2011-09-19 14:00:42 +00:00
|
|
|
self.report({'ERROR'}, "Select at least one mesh object")
|
2011-03-31 11:49:01 +00:00
|
|
|
return {'CANCELLED'}
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
for obj in mesh_objects:
|
2011-04-20 17:51:56 +00:00
|
|
|
fake_context["object"] = obj
|
2011-03-30 10:29:32 +00:00
|
|
|
# make each selected object a fluid
|
2011-04-20 17:51:56 +00:00
|
|
|
bpy.ops.object.modifier_add(fake_context, type='FLUID_SIMULATION')
|
2011-03-31 11:49:01 +00:00
|
|
|
|
2011-07-25 05:54:32 +00:00
|
|
|
# fluid has to be before constructive modifiers,
|
|
|
|
# so it might not be the last modifier
|
2011-03-31 11:49:01 +00:00
|
|
|
for mod in obj.modifiers:
|
|
|
|
if mod.type == 'FLUID_SIMULATION':
|
|
|
|
break
|
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
if self.style == 'INFLOW':
|
2011-03-31 11:49:01 +00:00
|
|
|
mod.settings.type = 'INFLOW'
|
2011-10-06 10:06:53 +00:00
|
|
|
mod.settings.inflow_velocity = self.initial_velocity
|
2011-03-30 10:29:32 +00:00
|
|
|
else:
|
2011-03-31 11:49:01 +00:00
|
|
|
mod.settings.type = 'FLUID'
|
2011-10-06 10:06:53 +00:00
|
|
|
mod.settings.initial_velocity = self.initial_velocity
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.hide_render = not self.show_flows
|
2011-03-30 13:35:54 +00:00
|
|
|
if not self.show_flows:
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.draw_type = 'WIRE'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# store bounding box min/max for the domain object
|
2011-03-31 11:49:01 +00:00
|
|
|
obj_bb_minmax(obj, min_co, max_co)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# add the fluid domain object
|
|
|
|
bpy.ops.mesh.primitive_cube_add()
|
2011-03-31 11:49:01 +00:00
|
|
|
obj = context.active_object
|
|
|
|
obj.name = "Fluid Domain"
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-07-25 05:54:32 +00:00
|
|
|
# give the fluid some room below the flows
|
|
|
|
# and scale with initial velocity
|
2011-03-31 11:49:01 +00:00
|
|
|
v = 0.5 * self.initial_velocity
|
2011-04-01 02:41:15 +00:00
|
|
|
obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v
|
2011-07-25 05:54:32 +00:00
|
|
|
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])))
|
|
|
|
)
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# setup smoke domain
|
2011-04-20 17:51:56 +00:00
|
|
|
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
|
2011-03-31 11:49:01 +00:00
|
|
|
obj.modifiers[-1].settings.type = 'DOMAIN'
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# make the domain smooth so it renders nicely
|
|
|
|
bpy.ops.object.shade_smooth()
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
# create a ray-transparent material for the domain
|
2011-04-20 17:51:56 +00:00
|
|
|
bpy.ops.object.material_slot_add()
|
2011-04-10 10:45:56 +00:00
|
|
|
|
|
|
|
mat = bpy.data.materials.new("Fluid Domain Material")
|
2011-04-04 14:35:22 +00:00
|
|
|
obj.material_slots[0].material = mat
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
mat.specular_intensity = 1
|
|
|
|
mat.specular_hardness = 100
|
|
|
|
mat.use_transparency = True
|
2011-04-01 02:41:15 +00:00
|
|
|
mat.alpha = 0.0
|
2011-03-30 10:29:32 +00:00
|
|
|
mat.transparency_method = 'RAYTRACE'
|
|
|
|
mat.raytrace_transparency.ior = 1.33
|
|
|
|
mat.raytrace_transparency.depth = 4
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-03-30 10:29:32 +00:00
|
|
|
if self.start_baking:
|
2012-09-03 16:08:28 +00:00
|
|
|
bpy.ops.fluid.bake('INVOKE_DEFAULT')
|
2011-03-30 15:02:02 +00:00
|
|
|
|
2011-06-15 00:16:30 +00:00
|
|
|
return {'FINISHED'}
|