Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Tönne <lukas.toenne@gmail.com>2016-12-28 19:30:58 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-12-28 19:30:58 +0300
commit6ecab6dd8e48d564a2b43e0e81e79d079e8b4c77 (patch)
tree618e2d24eb34a05a81f726dd52eb2b7468e9296d /release
parent605263177b8eea24c1449e4dbf0138175ec3dddf (diff)
Revert particle system and point cache removal in blender2.8 branch.
This reverts commit 5aa19be91263a249ffae75573e3b32f24269d890 and b4a721af694817fa921b119df83d33ede7d7fed0. Due to postponement of particle system rewrite it was decided to put particle code back into the 2.8 branch for the time being.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/presets/hair_dynamics/default.py17
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py233
-rw-r--r--release/scripts/startup/bl_operators/presets.py30
-rw-r--r--release/scripts/startup/bl_operators/view3d.py2
-rw-r--r--release/scripts/startup/bl_ui/__init__.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py34
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py1408
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py14
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py107
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py40
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py45
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_softbody.py14
-rw-r--r--release/scripts/startup/bl_ui/properties_scene.py21
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py77
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py2
-rw-r--r--release/scripts/startup/bl_ui/space_time.py1
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py115
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py105
20 files changed, 2235 insertions, 34 deletions
diff --git a/release/scripts/presets/hair_dynamics/default.py b/release/scripts/presets/hair_dynamics/default.py
new file mode 100644
index 00000000000..830d28a76f0
--- /dev/null
+++ b/release/scripts/presets/hair_dynamics/default.py
@@ -0,0 +1,17 @@
+import bpy
+psys = bpy.context.particle_system
+cloth = bpy.context.particle_system.cloth
+settings = bpy.context.particle_system.cloth.settings
+collision = bpy.context.particle_system.cloth.collision_settings
+
+settings.quality = 5
+settings.mass = 0.30000001192092896
+settings.bending_stiffness = 0.5
+psys.settings.bending_random = 0.0
+settings.bending_damping = 0.5
+settings.air_damping = 1.0
+settings.internal_friction = 0.0
+settings.density_target = 0.0
+settings.density_strength = 0.0
+settings.voxel_cell_size = 0.10000000149011612
+settings.pin_stiffness = 0.0
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 3e1bb54f043..cdab380bb9c 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -47,6 +47,239 @@ def object_ensure_material(obj, mat_name):
return mat
+class QuickFur(Operator):
+ bl_idname = "object.quick_fur"
+ bl_label = "Quick Fur"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ 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,
+ )
+
+ def execute(self, context):
+ fake_context = context.copy()
+ mesh_objects = [obj for obj in context.selected_objects
+ if obj.type == 'MESH' and obj.mode == 'OBJECT']
+
+ if not mesh_objects:
+ self.report({'ERROR'}, "Select at least one mesh object")
+ return {'CANCELLED'}
+
+ mat = bpy.data.materials.new("Fur Material")
+ mat.strand.tip_size = 0.25
+ mat.strand.blend_distance = 0.5
+
+ for obj in mesh_objects:
+ fake_context["object"] = obj
+ bpy.ops.object.particle_system_add(fake_context)
+
+ psys = obj.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'
+
+ obj.data.materials.append(mat)
+ psys.settings.material = len(obj.data.materials)
+
+ return {'FINISHED'}
+
+
+class QuickExplode(Operator):
+ bl_idname = "object.quick_explode"
+ bl_label = "Quick Explode"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ 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,
+ )
+
+ 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,
+ )
+
+ velocity = FloatProperty(
+ name="Outwards Velocity",
+ min=0, max=300000,
+ soft_min=0, soft_max=10,
+ default=1,
+ )
+
+ fade = BoolProperty(
+ name="Fade",
+ description="Fade the pieces over time",
+ default=True,
+ )
+
+ def execute(self, context):
+ fake_context = context.copy()
+ obj_act = context.active_object
+
+ if obj_act is None or obj_act.type != 'MESH':
+ 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)
+
+ if self.style == 'BLEND' and len(mesh_objects) != 2:
+ self.report({'ERROR'}, "Select two mesh objects")
+ self.style = 'EXPLODE'
+ return {'CANCELLED'}
+ elif not mesh_objects:
+ self.report({'ERROR'}, "Select at least one mesh object")
+ return {'CANCELLED'}
+
+ for obj in mesh_objects:
+ if obj.particle_systems:
+ self.report({'ERROR'},
+ "Object %r already has a "
+ "particle system" % obj.name)
+
+ 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
+
+ tex.color_ramp.elements[0].color[3] = 1.0
+ tex.color_ramp.elements[1].color[3] = 0.0
+
+ if self.style == 'BLEND':
+ from_obj = mesh_objects[1]
+ to_obj = mesh_objects[0]
+
+ 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
+ settings.frame_start = self.frame_start
+ settings.frame_end = self.frame_end - self.frame_duration
+ settings.lifetime = self.frame_duration
+ settings.normal_factor = self.velocity
+ settings.render_type = 'NONE'
+
+ explode = obj.modifiers.new(name='Explode', type='EXPLODE')
+ explode.use_edge_cut = True
+
+ if self.fade:
+ explode.show_dead = False
+ uv = obj.data.uv_textures.new("Explode fade")
+ explode.particle_uv = uv.name
+
+ mat = object_ensure_material(obj, "Explode Fade")
+
+ mat.use_transparency = True
+ mat.use_transparent_shadows = True
+ mat.alpha = 0.0
+ mat.specular_alpha = 0.0
+
+ 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:
+ tex_slot.alpha_factor = -1.0
+ elem = tex.color_ramp.elements[1]
+ else:
+ elem = tex.color_ramp.elements[0]
+ # Keep already defined alpha!
+ elem.color[:3] = mat.diffuse_color
+ 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
+ settings.angular_velocity_factor = self.velocity / 10.0
+
+ return {'FINISHED'}
+
+ 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)
+
+
def obj_bb_minmax(obj, min_co, max_co):
for i in range(0, 8):
bb_vec = obj.matrix_world * Vector(obj.bound_box[i])
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py
index ea2794195e3..e01e509b292 100644
--- a/release/scripts/startup/bl_operators/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -384,6 +384,36 @@ class AddPresetFluid(AddPresetBase, Operator):
preset_subdir = "fluid"
+class AddPresetHairDynamics(AddPresetBase, Operator):
+ """Add or remove a Hair Dynamics Preset"""
+ bl_idname = "particle.hair_dynamics_preset_add"
+ bl_label = "Add Hair Dynamics Preset"
+ preset_menu = "PARTICLE_MT_hair_dynamics_presets"
+
+ preset_defines = [
+ "psys = bpy.context.particle_system",
+ "cloth = bpy.context.particle_system.cloth",
+ "settings = bpy.context.particle_system.cloth.settings",
+ "collision = bpy.context.particle_system.cloth.collision_settings",
+ ]
+
+ preset_subdir = "hair_dynamics"
+
+ preset_values = [
+ "settings.quality",
+ "settings.mass",
+ "settings.bending_stiffness",
+ "psys.settings.bending_random",
+ "settings.bending_damping",
+ "settings.air_damping",
+ "settings.internal_friction",
+ "settings.density_target",
+ "settings.density_strength",
+ "settings.voxel_cell_size",
+ "settings.pin_stiffness",
+ ]
+
+
class AddPresetSunSky(AddPresetBase, Operator):
"""Add or remove a Sky & Atmosphere Preset"""
bl_idname = "lamp.sunsky_preset_add"
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index 1ef9354ed0e..df4a93bb87f 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -199,6 +199,8 @@ class VIEW3D_OT_select_or_deselect_all(Operator):
bpy.ops.armature.select_all(action='DESELECT')
elif active_object.mode == 'POSE':
bpy.ops.pose.select_all(action='DESELECT')
+ elif active_object.mode == 'PARTICLE_EDIT':
+ bpy.ops.particle.select_all(action='DESELECT')
else:
bpy.ops.object.select_all(action='DESELECT')
else:
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index ac150392109..2389be6787d 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -47,6 +47,7 @@ _modules = [
"properties_object",
"properties_paint_common",
"properties_grease_pencil_common",
+ "properties_particle",
"properties_physics_cloth",
"properties_physics_common",
"properties_physics_dynamicpaint",
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index ad357b10927..d66fb08bcd6 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -695,6 +695,40 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col = split.column()
+ def PARTICLE_INSTANCE(self, layout, ob, md):
+ layout.prop(md, "object")
+ layout.prop(md, "particle_system_index", text="Particle System")
+
+ split = layout.split()
+ col = split.column()
+ col.label(text="Create From:")
+ col.prop(md, "use_normal")
+ col.prop(md, "use_children")
+ col.prop(md, "use_size")
+
+ col = split.column()
+ col.label(text="Show Particles When:")
+ col.prop(md, "show_alive")
+ col.prop(md, "show_unborn")
+ col.prop(md, "show_dead")
+
+ layout.separator()
+
+ layout.prop(md, "use_path", text="Create Along Paths")
+
+ split = layout.split()
+ split.active = md.use_path
+ col = split.column()
+ col.row().prop(md, "axis", expand=True)
+ col.prop(md, "use_preserve_shape")
+
+ col = split.column()
+ col.prop(md, "position", slider=True)
+ col.prop(md, "random_position", text="Random", slider=True)
+
+ def PARTICLE_SYSTEM(self, layout, ob, md):
+ layout.label(text="Settings can be found inside the Particle context")
+
def SCREW(self, layout, ob, md):
split = layout.split()
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index c7bae0d87d6..09a3a19cbce 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -40,6 +40,8 @@ class UnifiedPaintPanel:
return toolsettings.image_paint
return None
+ elif context.particle_edit_object:
+ return toolsettings.particle_edit
return None
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
new file mode 100644
index 00000000000..4e2666d7e40
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -0,0 +1,1408 @@
+# ##### 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 #####
+
+# <pep8 compliant>
+import bpy
+from bpy.types import Panel, Menu
+from rna_prop_ui import PropertyPanel
+from bpy.app.translations import pgettext_iface as iface_
+
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ basic_force_field_settings_ui,
+ basic_force_field_falloff_ui,
+ )
+
+
+def particle_panel_enabled(context, psys):
+ if psys is None:
+ return True
+ phystype = psys.settings.physics_type
+ if psys.settings.type in {'EMITTER', 'REACTOR'} and phystype in {'NO', 'KEYED'}:
+ return True
+ else:
+ return (psys.point_cache.is_baked is False) and (not psys.is_edited) and (not context.particle_system_editable)
+
+
+def particle_panel_poll(cls, context):
+ psys = context.particle_system
+ engine = context.scene.render.engine
+ settings = 0
+
+ if psys:
+ settings = psys.settings
+ elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+ settings = context.space_data.pin_id
+
+ if not settings:
+ return False
+
+ return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES)
+
+
+def particle_get_settings(context):
+ if context.particle_system:
+ return context.particle_system.settings
+ elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+ return context.space_data.pin_id
+ return None
+
+
+class PARTICLE_MT_specials(Menu):
+ bl_label = "Particle Specials"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ props = layout.operator("particle.copy_particle_systems", text="Copy Active to Selected Objects")
+ props.use_active = True
+ props.remove_target_particles = False
+
+ props = layout.operator("particle.copy_particle_systems", text="Copy All to Selected Objects")
+ props.use_active = False
+ props.remove_target_particles = True
+
+ layout.operator("particle.duplicate_particle_system")
+
+
+class PARTICLE_MT_hair_dynamics_presets(Menu):
+ bl_label = "Hair Dynamics Presets"
+ preset_subdir = "hair_dynamics"
+ preset_operator = "script.execute_preset"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+ draw = Menu.draw_preset
+
+
+class ParticleButtonsPanel:
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "particle"
+
+ @classmethod
+ def poll(cls, context):
+ return particle_panel_poll(cls, context)
+
+
+def find_modifier(ob, psys):
+ for md in ob.modifiers:
+ if md.type == 'PARTICLE_SYSTEM':
+ if md.particle_system == psys:
+ return md
+
+
+class PARTICLE_UL_particle_systems(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
+ ob = data
+ psys = item
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ md = find_modifier(ob, psys)
+
+ layout.prop(psys, "name", text="", emboss=False, icon_value=icon)
+ if md:
+ layout.prop(md, "show_render", emboss=False, icon_only=True, icon='RESTRICT_RENDER_OFF' if md.show_render else 'RESTRICT_RENDER_ON')
+ layout.prop(md, "show_viewport", emboss=False, icon_only=True, icon='RESTRICT_VIEW_OFF' if md.show_viewport else 'RESTRICT_VIEW_ON')
+
+ elif self.layout_type == 'GRID':
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
+class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel):
+ bl_label = ""
+ bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+ @classmethod
+ def poll(cls, context):
+ engine = context.scene.render.engine
+ return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+
+ if context.scene.render.engine == 'BLENDER_GAME':
+ layout.label("Not available in the Game Engine")
+ return
+
+ ob = context.object
+ psys = context.particle_system
+ part = 0
+
+ if ob:
+ row = layout.row()
+
+ row.template_list("PARTICLE_UL_particle_systems", "particle_systems", ob, "particle_systems",
+ ob.particle_systems, "active_index", rows=1)
+
+ col = row.column(align=True)
+ col.operator("object.particle_system_add", icon='ZOOMIN', text="")
+ col.operator("object.particle_system_remove", icon='ZOOMOUT', text="")
+ col.menu("PARTICLE_MT_specials", icon='DOWNARROW_HLT', text="")
+
+ if psys is None:
+ part = particle_get_settings(context)
+
+ layout.operator("object.particle_system_add", icon='ZOOMIN', text="New")
+
+ if part is None:
+ return
+
+ layout.template_ID(context.space_data, "pin_id")
+
+ if part.is_fluid:
+ layout.label(text="Settings used for fluid")
+ return
+
+ layout.prop(part, "type", text="Type")
+
+ elif not psys.settings:
+ split = layout.split(percentage=0.32)
+
+ col = split.column()
+ col.label(text="Settings:")
+
+ col = split.column()
+ col.template_ID(psys, "settings", new="particle.new")
+ else:
+ part = psys.settings
+
+ split = layout.split(percentage=0.32)
+ col = split.column()
+ if part.is_fluid is False:
+ col.label(text="Settings:")
+ col.label(text="Type:")
+
+ col = split.column()
+ if part.is_fluid is False:
+ row = col.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.template_ID(psys, "settings", new="particle.new")
+
+ if part.is_fluid:
+ layout.label(text=iface_("%d fluid particles for this frame") % part.count, translate=False)
+ return
+
+ row = col.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.prop(part, "type", text="")
+ row.prop(psys, "seed")
+
+ if part:
+ split = layout.split(percentage=0.65)
+ if part.type == 'HAIR':
+ if psys is not None and psys.is_edited:
+ split.operator("particle.edited_clear", text="Free Edit")
+ else:
+ row = split.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.prop(part, "regrow_hair")
+ row.prop(part, "use_advanced_hair")
+ row = split.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.prop(part, "hair_step")
+ if psys is not None and psys.is_edited:
+ if psys.is_global_hair:
+ row = layout.row(align=True)
+ row.operator("particle.connect_hair").all = False
+ row.operator("particle.connect_hair", text="Connect All").all = True
+ else:
+ row = layout.row(align=True)
+ row.operator("particle.disconnect_hair").all = False
+ row.operator("particle.disconnect_hair", text="Disconnect All").all = True
+ elif psys is not None and part.type == 'REACTOR':
+ split.enabled = particle_panel_enabled(context, psys)
+ split.prop(psys, "reactor_target_object")
+ split.prop(psys, "reactor_target_particle_system", text="Particle System")
+
+
+class PARTICLE_PT_emission(ParticleButtonsPanel, Panel):
+ bl_label = "Emission"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ psys = context.particle_system
+ settings = particle_get_settings(context)
+
+ if settings is None:
+ return False
+ if settings.is_fluid:
+ return False
+ if particle_panel_poll(PARTICLE_PT_emission, context):
+ return psys is None or not context.particle_system.point_cache.use_external
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ layout.enabled = particle_panel_enabled(context, psys) and (psys is None or not psys.has_multiple_caches)
+
+ row = layout.row()
+ row.active = part.emit_from == 'VERT' or part.distribution != 'GRID'
+ row.prop(part, "count")
+
+ if part.type == 'HAIR':
+ row.prop(part, "hair_length")
+ if not part.use_advanced_hair:
+ row = layout.row()
+ row.prop(part, "use_modifier_stack")
+ return
+
+ if part.type != 'HAIR':
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.prop(part, "frame_start")
+ col.prop(part, "frame_end")
+
+ col = split.column(align=True)
+ col.prop(part, "lifetime")
+ col.prop(part, "lifetime_random", slider=True)
+
+ layout.label(text="Emit From:")
+ layout.prop(part, "emit_from", expand=True)
+
+ row = layout.row()
+ if part.emit_from == 'VERT':
+ row.prop(part, "use_emit_random")
+ elif part.distribution == 'GRID':
+ row.prop(part, "invert_grid")
+ row.prop(part, "hexagonal_grid")
+ else:
+ row.prop(part, "use_emit_random")
+ row.prop(part, "use_even_distribution")
+
+ if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
+ layout.prop(part, "distribution", expand=True)
+
+ row = layout.row()
+ if part.distribution == 'JIT':
+ row.prop(part, "userjit", text="Particles/Face")
+ row.prop(part, "jitter_factor", text="Jittering Amount", slider=True)
+ elif part.distribution == 'GRID':
+ row.prop(part, "grid_resolution")
+ row.prop(part, "grid_random", text="Random", slider=True)
+
+ row = layout.row()
+ row.prop(part, "use_modifier_stack")
+
+
+class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
+ bl_label = "Hair dynamics"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ psys = context.particle_system
+ engine = context.scene.render.engine
+ if psys is None:
+ return False
+ if psys.settings is None:
+ return False
+ return psys.settings.type == 'HAIR' and (engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
+ psys = context.particle_system
+ self.layout.prop(psys, "use_hair_dynamics", text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+
+ if not psys.cloth:
+ return
+
+ cloth_md = psys.cloth
+ cloth = cloth_md.settings
+ result = cloth_md.solver_result
+
+ layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
+
+ row = layout.row(align=True)
+ row.menu("PARTICLE_MT_hair_dynamics_presets", text=bpy.types.PARTICLE_MT_hair_dynamics_presets.bl_label)
+ row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMIN')
+ row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMOUT').remove_active = True
+
+ split = layout.column()
+
+ col = split.column()
+ col.label(text="Structure")
+ col.prop(cloth, "mass")
+ sub = col.column(align=True)
+ subsub = sub.row(align=True)
+ subsub.prop(cloth, "bending_stiffness", text="Stiffness")
+ subsub.prop(psys.settings, "bending_random", text="Random")
+ sub.prop(cloth, "bending_damping", text="Damping")
+ # XXX has no noticeable effect with stiff hair structure springs
+ #col.prop(cloth, "spring_damping", text="Damping")
+
+ split.separator()
+
+ col = split.column()
+ col.label(text="Volume")
+ col.prop(cloth, "air_damping", text="Air Drag")
+ col.prop(cloth, "internal_friction", slider=True)
+ sub = col.column(align=True)
+ sub.prop(cloth, "density_target", text="Density Target")
+ sub.prop(cloth, "density_strength", slider=True, text="Strength")
+ col.prop(cloth, "voxel_cell_size")
+
+ split.separator()
+
+ col = split.column()
+ col.label(text="Pinning")
+ col.prop(cloth, "pin_stiffness", text="Goal Strength")
+
+ split.separator()
+
+ col = split.column()
+ col.label(text="Quality:")
+ col.prop(cloth, "quality", text="Steps", slider=True)
+
+ row = col.row()
+ row.prop(psys.settings, "show_hair_grid", text="HairGrid")
+
+ if result:
+ box = layout.box()
+
+ if not result.status:
+ label = " "
+ icon = 'NONE'
+ elif result.status == {'SUCCESS'}:
+ label = "Success"
+ icon = 'NONE'
+ elif result.status - {'SUCCESS'} == {'NO_CONVERGENCE'}:
+ label = "No Convergence"
+ icon = 'ERROR'
+ else:
+ label = "ERROR"
+ icon = 'ERROR'
+ box.label(label, icon=icon)
+ box.label("Iterations: %d .. %d (avg. %d)" % (result.min_iterations, result.max_iterations, result.avg_iterations))
+ box.label("Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
+
+
+class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
+ bl_label = "Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ psys = context.particle_system
+ engine = context.scene.render.engine
+ if psys is None:
+ return False
+ if psys.settings is None:
+ return False
+ if psys.settings.is_fluid:
+ return False
+ phystype = psys.settings.physics_type
+ if phystype == 'NO' or phystype == 'KEYED':
+ return False
+ return (psys.settings.type in {'EMITTER', 'REACTOR'} or (psys.settings.type == 'HAIR' and (psys.use_hair_dynamics or psys.point_cache.is_baked))) and engine in cls.COMPAT_ENGINES
+
+ def draw(self, context):
+ psys = context.particle_system
+
+ point_cache_ui(self, context, psys.point_cache, True, 'HAIR' if (psys.settings.type == 'HAIR') else 'PSYS')
+
+
+class PARTICLE_PT_velocity(ParticleButtonsPanel, Panel):
+ bl_label = "Velocity"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ if particle_panel_poll(PARTICLE_PT_velocity, context):
+ psys = context.particle_system
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
+ return False
+ return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
+ else:
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ layout.enabled = particle_panel_enabled(context, psys)
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Emitter Geometry:")
+ col.prop(part, "normal_factor")
+ sub = col.column(align=True)
+ sub.prop(part, "tangent_factor")
+ sub.prop(part, "tangent_phase", slider=True)
+
+ col = split.column()
+ col.label(text="Emitter Object:")
+ col.prop(part, "object_align_factor", text="")
+
+ layout.label(text="Other:")
+ row = layout.row()
+ if part.emit_from == 'PARTICLE':
+ row.prop(part, "particle_factor")
+ else:
+ row.prop(part, "object_factor", slider=True)
+ row.prop(part, "factor_random")
+
+ #if part.type=='REACTOR':
+ # sub.prop(part, "reactor_factor")
+ # sub.prop(part, "reaction_shape", slider=True)
+
+
+class PARTICLE_PT_rotation(ParticleButtonsPanel, Panel):
+ bl_label = "Rotation"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ if particle_panel_poll(PARTICLE_PT_rotation, context):
+ psys = context.particle_system
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
+ return False
+ return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
+ else:
+ return False
+
+ def draw_header(self, context):
+ psys = context.particle_system
+ if psys:
+ part = psys.settings
+ else:
+ part = context.space_data.pin_id
+
+ self.layout.prop(part, "use_rotations", text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ if psys:
+ part = psys.settings
+ else:
+ part = context.space_data.pin_id
+
+ layout.enabled = particle_panel_enabled(context, psys) and part.use_rotations
+
+ layout.label(text="Initial Orientation:")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.prop(part, "rotation_mode", text="")
+ col.prop(part, "rotation_factor_random", slider=True, text="Random")
+
+ col = split.column(align=True)
+ col.prop(part, "phase_factor", slider=True)
+ col.prop(part, "phase_factor_random", text="Random", slider=True)
+
+ if part.type != 'HAIR':
+ layout.label(text="Angular Velocity:")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.prop(part, "angular_velocity_mode", text="")
+ sub = col.column(align=True)
+ sub.active = part.angular_velocity_mode != 'NONE'
+ sub.prop(part, "angular_velocity_factor", text="")
+
+ col = split.column()
+ col.prop(part, "use_dynamic_rotation")
+
+
+class PARTICLE_PT_physics(ParticleButtonsPanel, Panel):
+ bl_label = "Physics"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ if particle_panel_poll(PARTICLE_PT_physics, context):
+ psys = context.particle_system
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
+ return False
+ return psys is None or not psys.point_cache.use_external
+ else:
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ layout.enabled = particle_panel_enabled(context, psys)
+
+ layout.prop(part, "physics_type", expand=True)
+
+ row = layout.row()
+ col = row.column(align=True)
+ col.prop(part, "particle_size")
+ col.prop(part, "size_random", slider=True)
+
+ if part.physics_type != 'NO':
+ col = row.column(align=True)
+ col.prop(part, "mass")
+ col.prop(part, "use_multiply_size_mass", text="Multiply mass with size")
+
+ if part.physics_type in {'NEWTON', 'FLUID'}:
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Forces:")
+ col.prop(part, "brownian_factor")
+ col.prop(part, "drag_factor", slider=True)
+ col.prop(part, "damping", slider=True)
+
+ col = split.column()
+ col.label(text="Integration:")
+ col.prop(part, "integrator", text="")
+ col.prop(part, "timestep")
+ sub = col.row()
+ sub.prop(part, "subframes")
+ supports_courant = part.physics_type == 'FLUID'
+ subsub = sub.row()
+ subsub.enabled = supports_courant
+ subsub.prop(part, "use_adaptive_subframes", text="")
+ if supports_courant and part.use_adaptive_subframes:
+ col.prop(part, "courant_target", text="Threshold")
+
+ row = layout.row()
+ row.prop(part, "use_size_deflect")
+ row.prop(part, "use_die_on_collision")
+
+ layout.prop(part, "collision_group")
+
+ if part.physics_type == 'FLUID':
+ fluid = part.fluid
+
+ split = layout.split()
+ sub = split.row()
+ sub.prop(fluid, "solver", expand=True)
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Fluid properties:")
+ col.prop(fluid, "stiffness", text="Stiffness")
+ col.prop(fluid, "linear_viscosity", text="Viscosity")
+ col.prop(fluid, "buoyancy", text="Buoyancy", slider=True)
+
+ col = split.column()
+ col.label(text="Advanced:")
+
+ if fluid.solver == 'DDR':
+ sub = col.row()
+ sub.prop(fluid, "repulsion", slider=fluid.factor_repulsion)
+ sub.prop(fluid, "factor_repulsion", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "stiff_viscosity", slider=fluid.factor_stiff_viscosity)
+ sub.prop(fluid, "factor_stiff_viscosity", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "fluid_radius", slider=fluid.factor_radius)
+ sub.prop(fluid, "factor_radius", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "rest_density", slider=fluid.use_factor_density)
+ sub.prop(fluid, "use_factor_density", text="")
+
+ if fluid.solver == 'CLASSICAL':
+ # With the classical solver, it is possible to calculate the
+ # spacing between particles when the fluid is at rest. This
+ # makes it easier to set stable initial conditions.
+ particle_volume = part.mass / fluid.rest_density
+ spacing = pow(particle_volume, 1.0 / 3.0)
+ sub = col.row()
+ sub.label(text="Spacing: %g" % spacing)
+
+ elif fluid.solver == 'DDR':
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Springs:")
+ col.prop(fluid, "spring_force", text="Force")
+ col.prop(fluid, "use_viscoelastic_springs")
+ sub = col.column(align=True)
+ sub.active = fluid.use_viscoelastic_springs
+ sub.prop(fluid, "yield_ratio", slider=True)
+ sub.prop(fluid, "plasticity", slider=True)
+
+ col = split.column()
+ col.label(text="Advanced:")
+ sub = col.row()
+ sub.prop(fluid, "rest_length", slider=fluid.factor_rest_length)
+ sub.prop(fluid, "factor_rest_length", text="")
+ col.label(text="")
+ sub = col.column()
+ sub.active = fluid.use_viscoelastic_springs
+ sub.prop(fluid, "use_initial_rest_length")
+ sub.prop(fluid, "spring_frames", text="Frames")
+
+ elif part.physics_type == 'KEYED':
+ split = layout.split()
+ sub = split.column()
+
+ row = layout.row()
+ col = row.column()
+ col.active = not psys.use_keyed_timing
+ col.prop(part, "keyed_loops", text="Loops")
+ if psys:
+ row.prop(psys, "use_keyed_timing", text="Use Timing")
+
+ layout.label(text="Keys:")
+ elif part.physics_type == 'BOIDS':
+ boids = part.boids
+
+ row = layout.row()
+ row.prop(boids, "use_flight")
+ row.prop(boids, "use_land")
+ row.prop(boids, "use_climb")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.active = boids.use_flight
+ col.prop(boids, "air_speed_max")
+ col.prop(boids, "air_speed_min", slider=True)
+ col.prop(boids, "air_acc_max", slider=True)
+ col.prop(boids, "air_ave_max", slider=True)
+ col.prop(boids, "air_personal_space")
+ row = col.row(align=True)
+ row.active = (boids.use_land or boids.use_climb) and boids.use_flight
+ row.prop(boids, "land_smooth")
+
+ col = split.column(align=True)
+ col.active = boids.use_land or boids.use_climb
+ col.prop(boids, "land_speed_max")
+ col.prop(boids, "land_jump_speed")
+ col.prop(boids, "land_acc_max", slider=True)
+ col.prop(boids, "land_ave_max", slider=True)
+ col.prop(boids, "land_personal_space")
+ col.prop(boids, "land_stick_force")
+
+ layout.prop(part, "collision_group")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.label(text="Battle:")
+ col.prop(boids, "health")
+ col.prop(boids, "strength")
+ col.prop(boids, "aggression")
+ col.prop(boids, "accuracy")
+ col.prop(boids, "range")
+
+ col = split.column()
+ col.label(text="Misc:")
+ col.prop(boids, "bank", slider=True)
+ col.prop(boids, "pitch", slider=True)
+ col.prop(boids, "height", slider=True)
+
+ if psys and part.physics_type in {'KEYED', 'BOIDS', 'FLUID'}:
+ if part.physics_type == 'BOIDS':
+ layout.label(text="Relations:")
+ elif part.physics_type == 'FLUID':
+ layout.label(text="Fluid interaction:")
+
+ row = layout.row()
+ row.template_list("UI_UL_list", "particle_targets", psys, "targets", psys, "active_particle_target_index", rows=4)
+
+ col = row.column()
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("particle.new_target", icon='ZOOMIN', text="")
+ subsub.operator("particle.target_remove", icon='ZOOMOUT', text="")
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("particle.target_move_up", icon='MOVE_UP_VEC', text="")
+ subsub.operator("particle.target_move_down", icon='MOVE_DOWN_VEC', text="")
+
+ key = psys.active_particle_target
+ if key:
+ row = layout.row()
+ if part.physics_type == 'KEYED':
+ col = row.column()
+ #doesn't work yet
+ #col.alert = key.valid
+ col.prop(key, "object", text="")
+ col.prop(key, "system", text="System")
+ col = row.column()
+ col.active = psys.use_keyed_timing
+ col.prop(key, "time")
+ col.prop(key, "duration")
+ elif part.physics_type == 'BOIDS':
+ sub = row.row()
+ #doesn't work yet
+ #sub.alert = key.valid
+ sub.prop(key, "object", text="")
+ sub.prop(key, "system", text="System")
+
+ layout.prop(key, "alliance", expand=True)
+ elif part.physics_type == 'FLUID':
+ sub = row.row()
+ #doesn't work yet
+ #sub.alert = key.valid
+ sub.prop(key, "object", text="")
+ sub.prop(key, "system", text="System")
+
+
+class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel):
+ bl_label = "Boid Brain"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ psys = context.particle_system
+ settings = particle_get_settings(context)
+ engine = context.scene.render.engine
+
+ if settings is None:
+ return False
+ if psys is not None and psys.point_cache.use_external:
+ return False
+ return settings.physics_type == 'BOIDS' and engine in cls.COMPAT_ENGINES
+
+ def draw(self, context):
+ layout = self.layout
+
+ boids = particle_get_settings(context).boids
+
+ layout.enabled = particle_panel_enabled(context, context.particle_system)
+
+ # Currently boids can only use the first state so these are commented out for now.
+ #row = layout.row()
+ #row.template_list("UI_UL_list", "particle_boids", boids, "states",
+ # boids, "active_boid_state_index", compact="True")
+ #col = row.row()
+ #sub = col.row(align=True)
+ #sub.operator("boid.state_add", icon='ZOOMIN', text="")
+ #sub.operator("boid.state_del", icon='ZOOMOUT', text="")
+ #sub = row.row(align=True)
+ #sub.operator("boid.state_move_up", icon='MOVE_UP_VEC', text="")
+ #sub.operator("boid.state_move_down", icon='MOVE_DOWN_VEC', text="")
+
+ state = boids.active_boid_state
+
+ #layout.prop(state, "name", text="State name")
+
+ row = layout.row()
+ row.prop(state, "ruleset_type")
+ if state.ruleset_type == 'FUZZY':
+ row.prop(state, "rule_fuzzy", slider=True)
+ else:
+ row.label(text="")
+
+ row = layout.row()
+ row.template_list("UI_UL_list", "particle_boids_rules", state, "rules", state, "active_boid_rule_index", rows=4)
+
+ col = row.column()
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator_menu_enum("boid.rule_add", "type", icon='ZOOMIN', text="")
+ subsub.operator("boid.rule_del", icon='ZOOMOUT', text="")
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("boid.rule_move_up", icon='MOVE_UP_VEC', text="")
+ subsub.operator("boid.rule_move_down", icon='MOVE_DOWN_VEC', text="")
+
+ rule = state.active_boid_rule
+
+ if rule:
+ row = layout.row()
+ row.prop(rule, "name", text="")
+ #somebody make nice icons for boids here please! -jahka
+ row.prop(rule, "use_in_air", icon='MOVE_UP_VEC', text="")
+ row.prop(rule, "use_on_land", icon='MOVE_DOWN_VEC', text="")
+
+ row = layout.row()
+
+ if rule.type == 'GOAL':
+ row.prop(rule, "object")
+ row = layout.row()
+ row.prop(rule, "use_predict")
+ elif rule.type == 'AVOID':
+ row.prop(rule, "object")
+ row = layout.row()
+ row.prop(rule, "use_predict")
+ row.prop(rule, "fear_factor")
+ elif rule.type == 'FOLLOW_PATH':
+ row.label(text="Not yet functional")
+ elif rule.type == 'AVOID_COLLISION':
+ row.prop(rule, "use_avoid")
+ row.prop(rule, "use_avoid_collision")
+ row.prop(rule, "look_ahead")
+ elif rule.type == 'FOLLOW_LEADER':
+ row.prop(rule, "object", text="")
+ row.prop(rule, "distance")
+ row = layout.row()
+ row.prop(rule, "use_line")
+ sub = row.row()
+ sub.active = rule.line
+ sub.prop(rule, "queue_count")
+ elif rule.type == 'AVERAGE_SPEED':
+ row.prop(rule, "speed", slider=True)
+ row.prop(rule, "wander", slider=True)
+ row.prop(rule, "level", slider=True)
+ elif rule.type == 'FIGHT':
+ row.prop(rule, "distance")
+ row.prop(rule, "flee_distance")
+
+
+class PARTICLE_PT_render(ParticleButtonsPanel, Panel):
+ bl_label = "Render"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ settings = particle_get_settings(context)
+ engine = context.scene.render.engine
+ if settings is None:
+ return False
+
+ return engine in cls.COMPAT_ENGINES
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ if psys:
+ row = layout.row()
+ if part.render_type in {'OBJECT', 'GROUP'}:
+ row.enabled = False
+ row.prop(part, "material_slot", text="")
+ row.prop(psys, "parent")
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(part, "use_render_emitter")
+ col.prop(part, "use_parent_particles")
+
+ col = split.column()
+ col.prop(part, "show_unborn")
+ col.prop(part, "use_dead")
+
+ layout.prop(part, "render_type", expand=True)
+
+ split = layout.split()
+
+ col = split.column()
+
+ if part.render_type == 'LINE':
+ col.prop(part, "line_length_tail")
+ col.prop(part, "line_length_head")
+
+ split.prop(part, "use_velocity_length")
+ elif part.render_type == 'PATH':
+ col.prop(part, "use_strand_primitive")
+ sub = col.column()
+ sub.active = (part.use_strand_primitive is False)
+ sub.prop(part, "use_render_adaptive")
+ sub = col.column()
+ sub.active = part.use_render_adaptive or part.use_strand_primitive is True
+ sub.prop(part, "adaptive_angle")
+ sub = col.column()
+ sub.active = (part.use_render_adaptive is True and part.use_strand_primitive is False)
+ sub.prop(part, "adaptive_pixel")
+ col.prop(part, "use_hair_bspline")
+ col.prop(part, "render_step", text="Steps")
+
+ col = split.column()
+ col.label(text="Timing:")
+ col.prop(part, "use_absolute_path_time")
+
+ if part.type == 'HAIR' or psys.point_cache.is_baked:
+ col.prop(part, "path_start", text="Start", slider=not part.use_absolute_path_time)
+ else:
+ col.prop(part, "trail_count")
+
+ col.prop(part, "path_end", text="End", slider=not part.use_absolute_path_time)
+ col.prop(part, "length_random", text="Random", slider=True)
+
+ row = layout.row()
+ col = row.column()
+
+ if part.type == 'HAIR' and part.use_strand_primitive is True and part.child_type == 'INTERPOLATED':
+ layout.prop(part, "use_simplify")
+ if part.use_simplify is True:
+ row = layout.row()
+ row.prop(part, "simplify_refsize")
+ row.prop(part, "simplify_rate")
+ row.prop(part, "simplify_transition")
+ row = layout.row()
+ row.prop(part, "use_simplify_viewport")
+ sub = row.row()
+ sub.active = part.use_simplify_viewport is True
+ sub.prop(part, "simplify_viewport")
+
+ elif part.render_type == 'OBJECT':
+ col.prop(part, "dupli_object")
+ sub = col.row()
+ sub.prop(part, "use_global_dupli")
+ sub.prop(part, "use_rotation_dupli")
+ sub.prop(part, "use_scale_dupli")
+ elif part.render_type == 'GROUP':
+ col.prop(part, "dupli_group")
+ split = layout.split()
+
+ col = split.column()
+ col.prop(part, "use_whole_group")
+ sub = col.column()
+ sub.active = (part.use_whole_group is False)
+ sub.prop(part, "use_group_pick_random")
+ sub.prop(part, "use_group_count")
+
+ col = split.column()
+ sub = col.column()
+ sub.active = (part.use_whole_group is False)
+ sub.prop(part, "use_global_dupli")
+ sub.prop(part, "use_rotation_dupli")
+ sub.prop(part, "use_scale_dupli")
+
+ if part.use_group_count and not part.use_whole_group:
+ row = layout.row()
+ row.template_list("UI_UL_list", "particle_dupli_weights", part, "dupli_weights",
+ part, "active_dupliweight_index")
+
+ col = row.column()
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("particle.dupliob_copy", icon='ZOOMIN', text="")
+ subsub.operator("particle.dupliob_remove", icon='ZOOMOUT', text="")
+ subsub.operator("particle.dupliob_move_up", icon='MOVE_UP_VEC', text="")
+ subsub.operator("particle.dupliob_move_down", icon='MOVE_DOWN_VEC', text="")
+
+ weight = part.active_dupliweight
+ if weight:
+ row = layout.row()
+ row.prop(weight, "count")
+
+ elif part.render_type == 'BILLBOARD':
+ ob = context.object
+
+ col.label(text="Align:")
+
+ row = layout.row()
+ row.prop(part, "billboard_align", expand=True)
+ row.prop(part, "lock_billboard", text="Lock")
+ row = layout.row()
+ row.prop(part, "billboard_object")
+
+ row = layout.row()
+ col = row.column(align=True)
+ col.label(text="Tilt:")
+ col.prop(part, "billboard_tilt", text="Angle", slider=True)
+ col.prop(part, "billboard_tilt_random", text="Random", slider=True)
+ col = row.column()
+ col.prop(part, "billboard_offset")
+
+ row = layout.row()
+ col = row.column()
+ col.prop(part, "billboard_size", text="Scale")
+ if part.billboard_align == 'VEL':
+ col = row.column(align=True)
+ col.label("Velocity Scale:")
+ col.prop(part, "billboard_velocity_head", text="Head")
+ col.prop(part, "billboard_velocity_tail", text="Tail")
+
+ if psys:
+ col = layout.column()
+ col.prop_search(psys, "billboard_normal_uv", ob.data, "uv_textures")
+ col.prop_search(psys, "billboard_time_index_uv", ob.data, "uv_textures")
+
+ split = layout.split(percentage=0.33)
+ split.label(text="Split UVs:")
+ split.prop(part, "billboard_uv_split", text="Number of splits")
+
+ if psys:
+ col = layout.column()
+ col.active = part.billboard_uv_split > 1
+ col.prop_search(psys, "billboard_split_uv", ob.data, "uv_textures")
+
+ row = col.row()
+ row.label(text="Animate:")
+ row.prop(part, "billboard_animation", text="")
+ row.label(text="Offset:")
+ row.prop(part, "billboard_offset_split", text="")
+
+ if part.render_type == 'HALO' or part.render_type == 'LINE' or part.render_type == 'BILLBOARD':
+ row = layout.row()
+ col = row.column()
+ col.prop(part, "trail_count")
+ if part.trail_count > 1:
+ col.prop(part, "use_absolute_path_time", text="Length in frames")
+ col = row.column()
+ col.prop(part, "path_end", text="Length", slider=not part.use_absolute_path_time)
+ col.prop(part, "length_random", text="Random", slider=True)
+ else:
+ col = row.column()
+ col.label(text="")
+
+ if part.render_type in {'OBJECT', 'GROUP'} and not part.use_advanced_hair:
+ row = layout.row(align=True)
+ row.prop(part, "particle_size")
+ row.prop(part, "size_random", slider=True)
+
+
+class PARTICLE_PT_draw(ParticleButtonsPanel, Panel):
+ bl_label = "Display"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ settings = particle_get_settings(context)
+ engine = context.scene.render.engine
+ if settings is None:
+ return False
+ return engine in cls.COMPAT_ENGINES
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ row = layout.row()
+ row.prop(part, "draw_method", expand=True)
+ row.prop(part, "show_guide_hairs")
+
+ if part.draw_method == 'NONE' or (part.render_type == 'NONE' and part.draw_method == 'RENDER'):
+ return
+
+ path = (part.render_type == 'PATH' and part.draw_method == 'RENDER') or part.draw_method == 'PATH'
+
+ row = layout.row()
+ row.prop(part, "draw_percentage", slider=True)
+ if part.draw_method != 'RENDER' or part.render_type == 'HALO':
+ row.prop(part, "draw_size")
+ else:
+ row.label(text="")
+
+ if part.draw_percentage != 100 and psys is not None:
+ if part.type == 'HAIR':
+ if psys.use_hair_dynamics and psys.point_cache.is_baked is False:
+ layout.row().label(text="Display percentage makes dynamics inaccurate without baking!")
+ else:
+ phystype = part.physics_type
+ if phystype != 'NO' and phystype != 'KEYED' and psys.point_cache.is_baked is False:
+ layout.row().label(text="Display percentage makes dynamics inaccurate without baking!")
+
+ row = layout.row()
+ col = row.column()
+ col.prop(part, "show_size")
+ col.prop(part, "show_velocity")
+ col.prop(part, "show_number")
+ if part.physics_type == 'BOIDS':
+ col.prop(part, "show_health")
+
+ col = row.column(align=True)
+ col.label(text="Color:")
+ col.prop(part, "draw_color", text="")
+ sub = col.row(align=True)
+ sub.active = (part.draw_color in {'VELOCITY', 'ACCELERATION'})
+ sub.prop(part, "color_maximum", text="Max")
+
+ if path:
+ col.prop(part, "draw_step")
+
+
+class PARTICLE_PT_children(ParticleButtonsPanel, Panel):
+ bl_label = "Children"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ return particle_panel_poll(cls, context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ psys = context.particle_system
+ part = particle_get_settings(context)
+
+ layout.row().prop(part, "child_type", expand=True)
+
+ if part.child_type == 'NONE':
+ return
+
+ row = layout.row()
+
+ col = row.column(align=True)
+ col.prop(part, "child_nbr", text="Display")
+ col.prop(part, "rendered_child_count", text="Render")
+
+ if part.child_type == 'INTERPOLATED':
+ col = row.column()
+ if psys:
+ col.prop(psys, "child_seed", text="Seed")
+ col.prop(part, "virtual_parents", slider=True)
+ col.prop(part, "create_long_hair_children")
+ else:
+ col = row.column(align=True)
+ col.prop(part, "child_size", text="Size")
+ col.prop(part, "child_size_random", text="Random")
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Effects:")
+
+ sub = col.column(align=True)
+ sub.prop(part, "use_clump_curve")
+ if part.use_clump_curve:
+ sub.template_curve_mapping(part, "clump_curve")
+ else:
+ sub.prop(part, "clump_factor", slider=True)
+ sub.prop(part, "clump_shape", slider=True)
+ sub = col.column(align=True)
+ sub.prop(part, "use_clump_noise")
+ subsub = sub.column()
+ subsub.enabled = part.use_clump_noise
+ subsub.prop(part, "clump_noise_size")
+
+ sub = col.column(align=True)
+ sub.prop(part, "child_length", slider=True)
+ sub.prop(part, "child_length_threshold", slider=True)
+
+ if part.child_type == 'SIMPLE':
+ sub = col.column(align=True)
+ sub.prop(part, "child_radius", text="Radius")
+ sub.prop(part, "child_roundness", text="Roundness", slider=True)
+ if psys:
+ sub.prop(psys, "child_seed", text="Seed")
+ elif part.virtual_parents > 0.0:
+ sub = col.column(align=True)
+ sub.label(text="Parting not")
+ sub.label(text="available with")
+ sub.label(text="virtual parents")
+ else:
+ sub = col.column(align=True)
+ sub.prop(part, "child_parting_factor", text="Parting", slider=True)
+ sub.prop(part, "child_parting_min", text="Min")
+ sub.prop(part, "child_parting_max", text="Max")
+
+ col = split.column()
+
+ col.prop(part, "use_roughness_curve")
+ if part.use_roughness_curve:
+ sub = col.column()
+ sub.template_curve_mapping(part, "roughness_curve")
+ sub.prop(part, "roughness_1", text="Roughness")
+ sub.prop(part, "roughness_1_size", text="Size")
+ else:
+ col.label(text="Roughness:")
+
+ sub = col.column(align=True)
+ sub.prop(part, "roughness_1", text="Uniform")
+ sub.prop(part, "roughness_1_size", text="Size")
+
+ sub = col.column(align=True)
+ sub.prop(part, "roughness_endpoint", "Endpoint")
+ sub.prop(part, "roughness_end_shape")
+
+ sub = col.column(align=True)
+ sub.prop(part, "roughness_2", text="Random")
+ sub.prop(part, "roughness_2_size", text="Size")
+ sub.prop(part, "roughness_2_threshold", slider=True)
+
+ layout.row().label(text="Kink:")
+ layout.row().prop(part, "kink", expand=True)
+
+ split = layout.split()
+ split.active = part.kink != 'NO'
+
+ if part.kink == 'SPIRAL':
+ col = split.column()
+ sub = col.column(align=True)
+ sub.prop(part, "kink_amplitude", text="Radius")
+ sub.prop(part, "kink_amplitude_random", text="Random", slider=True)
+ sub = col.column(align=True)
+ sub.prop(part, "kink_axis")
+ sub.prop(part, "kink_axis_random", text="Random", slider=True)
+ col = split.column(align=True)
+ col.prop(part, "kink_frequency", text="Frequency")
+ col.prop(part, "kink_shape", text="Shape", slider=True)
+ col.prop(part, "kink_extra_steps", text="Steps")
+ else:
+ col = split.column()
+ sub = col.column(align=True)
+ sub.prop(part, "kink_amplitude")
+ sub.prop(part, "kink_amplitude_clump", text="Clump", slider=True)
+ col.prop(part, "kink_flat", slider=True)
+ col = split.column(align=True)
+ col.prop(part, "kink_frequency")
+ col.prop(part, "kink_shape", slider=True)
+
+
+class PARTICLE_PT_field_weights(ParticleButtonsPanel, Panel):
+ bl_label = "Field Weights"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ return particle_panel_poll(cls, context)
+
+ def draw(self, context):
+ part = particle_get_settings(context)
+ effector_weights_ui(self, context, part.effector_weights, 'PSYS')
+
+ if part.type == 'HAIR':
+ row = self.layout.row()
+ row.prop(part.effector_weights, "apply_to_hair_growing")
+ row.prop(part, "apply_effector_to_children")
+ row = self.layout.row()
+ row.prop(part, "effect_hair", slider=True)
+
+
+class PARTICLE_PT_force_fields(ParticleButtonsPanel, Panel):
+ bl_label = "Force Field Settings"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ part = particle_get_settings(context)
+
+ row = layout.row()
+ row.prop(part, "use_self_effect")
+ row.prop(part, "effector_amount", text="Amount")
+
+ split = layout.split(percentage=0.2)
+ split.label(text="Type 1:")
+ split.prop(part.force_field_1, "type", text="")
+ basic_force_field_settings_ui(self, context, part.force_field_1)
+ if part.force_field_1.type != 'NONE':
+ layout.label(text="Falloff:")
+ basic_force_field_falloff_ui(self, context, part.force_field_1)
+
+ if part.force_field_1.type != 'NONE':
+ layout.label(text="")
+
+ split = layout.split(percentage=0.2)
+ split.label(text="Type 2:")
+ split.prop(part.force_field_2, "type", text="")
+ basic_force_field_settings_ui(self, context, part.force_field_2)
+ if part.force_field_2.type != 'NONE':
+ layout.label(text="Falloff:")
+ basic_force_field_falloff_ui(self, context, part.force_field_2)
+
+
+class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):
+ bl_label = "Vertex Groups"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ if context.particle_system is None:
+ return False
+ return particle_panel_poll(cls, context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ psys = context.particle_system
+
+ col = layout.column()
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_density", ob, "vertex_groups", text="Density")
+ row.prop(psys, "invert_vertex_group_density", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_length", ob, "vertex_groups", text="Length")
+ row.prop(psys, "invert_vertex_group_length", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_clump", ob, "vertex_groups", text="Clump")
+ row.prop(psys, "invert_vertex_group_clump", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_kink", ob, "vertex_groups", text="Kink")
+ row.prop(psys, "invert_vertex_group_kink", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_roughness_1", ob, "vertex_groups", text="Roughness 1")
+ row.prop(psys, "invert_vertex_group_roughness_1", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_roughness_2", ob, "vertex_groups", text="Roughness 2")
+ row.prop(psys, "invert_vertex_group_roughness_2", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ row = col.row(align=True)
+ row.prop_search(psys, "vertex_group_roughness_end", ob, "vertex_groups", text="Roughness End")
+ row.prop(psys, "invert_vertex_group_roughness_end", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+ # Commented out vertex groups don't work and are still waiting for better implementation
+ # row = layout.row()
+ # row.prop_search(psys, "vertex_group_velocity", ob, "vertex_groups", text="Velocity")
+ # row.prop(psys, "invert_vertex_group_velocity", text="")
+
+ # row = layout.row()
+ # row.prop_search(psys, "vertex_group_size", ob, "vertex_groups", text="Size")
+ # row.prop(psys, "invert_vertex_group_size", text="")
+
+ # row = layout.row()
+ # row.prop_search(psys, "vertex_group_tangent", ob, "vertex_groups", text="Tangent")
+ # row.prop(psys, "invert_vertex_group_tangent", text="")
+
+ # row = layout.row()
+ # row.prop_search(psys, "vertex_group_rotation", ob, "vertex_groups", text="Rotation")
+ # row.prop(psys, "invert_vertex_group_rotation", text="")
+
+ # row = layout.row()
+ # row.prop_search(psys, "vertex_group_field", ob, "vertex_groups", text="Field")
+ # row.prop(psys, "invert_vertex_group_field", text="")
+
+
+class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel, Panel):
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+ _context_path = "particle_system.settings"
+ _property_type = bpy.types.ParticleSettings
+
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index 0362cc42371..54581c9276d 100644
--- a/release/scripts/startup/bl_ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -21,13 +21,13 @@ import bpy
from bpy.types import Menu, Panel
from bl_ui.properties_physics_common import (
+ point_cache_ui,
effector_weights_ui,
)
def cloth_panel_enabled(md):
- return True
- #return md.point_cache.is_baked is False
+ return md.point_cache.is_baked is False
class CLOTH_MT_presets(Menu):
@@ -132,6 +132,16 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
sub.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="")
+class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, Panel):
+ bl_label = "Cloth Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ md = context.cloth
+ point_cache_ui(self, context, md.point_cache, cloth_panel_enabled(md), 'CLOTH')
+
+
class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
bl_label = "Cloth Collision"
bl_options = {'DEFAULT_CLOSED'}
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index ea4bbc76f43..277b59d187d 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -98,6 +98,113 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
'CONSTRAINT') # RB_TODO needs better icon
+# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc
+
+def point_cache_ui(self, context, cache, enabled, cachetype):
+ layout = self.layout
+
+ layout.context_pointer_set("point_cache", cache)
+
+ if not cachetype == 'RIGID_BODY':
+ row = layout.row()
+ row.template_list("UI_UL_list", "point_caches", cache, "point_caches",
+ cache.point_caches, "active_index", rows=1)
+ col = row.column(align=True)
+ col.operator("ptcache.add", icon='ZOOMIN', text="")
+ col.operator("ptcache.remove", icon='ZOOMOUT', text="")
+
+ row = layout.row()
+ if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
+ row.prop(cache, "use_external")
+
+ if cachetype == 'SMOKE':
+ row.prop(cache, "use_library_path", "Use Lib Path")
+
+ if cache.use_external:
+ split = layout.split(percentage=0.35)
+ col = split.column()
+ col.label(text="Index Number:")
+ col.label(text="File Path:")
+
+ col = split.column()
+ col.prop(cache, "index", text="")
+ col.prop(cache, "filepath", text="")
+
+ cache_info = cache.info
+ if cache_info:
+ layout.label(text=cache_info)
+ else:
+ if cachetype in {'SMOKE', 'DYNAMIC_PAINT'}:
+ if not bpy.data.is_saved:
+ layout.label(text="Cache is disabled until the file is saved")
+ layout.enabled = False
+
+ if not cache.use_external or cachetype == 'SMOKE':
+ row = layout.row(align=True)
+
+ if cachetype not in {'PSYS', 'DYNAMIC_PAINT'}:
+ row.enabled = enabled
+ row.prop(cache, "frame_start")
+ row.prop(cache, "frame_end")
+ if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+ row.prop(cache, "frame_step")
+
+ if cachetype != 'SMOKE':
+ layout.label(text=cache.info)
+
+ can_bake = True
+
+ if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+ split = layout.split()
+ split.enabled = enabled and bpy.data.is_saved
+
+ col = split.column()
+ col.prop(cache, "use_disk_cache")
+
+ col = split.column()
+ col.active = cache.use_disk_cache
+ col.prop(cache, "use_library_path", "Use Lib Path")
+
+ row = layout.row()
+ row.enabled = enabled and bpy.data.is_saved
+ row.active = cache.use_disk_cache
+ row.label(text="Compression:")
+ row.prop(cache, "compression", expand=True)
+
+ layout.separator()
+
+ if cache.id_data.library and not cache.use_disk_cache:
+ can_bake = False
+
+ col = layout.column(align=True)
+ col.label(text="Linked object baking requires Disk Cache to be enabled", icon='INFO')
+ else:
+ layout.separator()
+
+ split = layout.split()
+ split.active = can_bake
+
+ col = split.column()
+
+ if cache.is_baked is True:
+ col.operator("ptcache.free_bake", text="Free Bake")
+ else:
+ col.operator("ptcache.bake", text="Bake").bake = True
+
+ sub = col.row()
+ sub.enabled = (cache.is_frame_skip or cache.is_outdated) and enabled
+ sub.operator("ptcache.bake", text="Calculate To Frame").bake = False
+
+ sub = col.column()
+ sub.enabled = enabled
+ sub.operator("ptcache.bake_from_cache", text="Current Cache to Bake")
+
+ col = split.column()
+ col.operator("ptcache.bake_all", text="Bake All Dynamics").bake = True
+ col.operator("ptcache.free_bake_all", text="Free All Bakes")
+ col.operator("ptcache.bake_all", text="Update All To Frame").bake = False
+
+
def effector_weights_ui(self, context, weights, weight_type):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
index b3640463224..6c3a3246cf6 100644
--- a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
+++ b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
@@ -21,6 +21,7 @@ import bpy
from bpy.types import Panel, UIList
from bl_ui.properties_physics_common import (
+ point_cache_ui,
effector_weights_ui,
)
@@ -124,8 +125,10 @@ class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, Panel):
col = split.column()
if not use_shading_nodes:
- col.prop(brush, "use_material")
- if brush.use_material and not use_shading_nodes:
+ sub = col.column()
+ sub.active = (brush.paint_source != 'PARTICLE_SYSTEM')
+ sub.prop(brush, "use_material")
+ if brush.use_material and brush.paint_source != 'PARTICLE_SYSTEM' and not use_shading_nodes:
col.prop(brush, "material", text="")
col.prop(brush, "paint_alpha", text="Alpha Factor")
else:
@@ -390,6 +393,29 @@ class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
row.prop(surface, "shrink_speed")
+class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
+ bl_label = "Dynamic Paint Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ md = context.dynamic_paint
+ rd = context.scene.render
+ return (md and
+ md.ui_type == 'CANVAS' and
+ md.canvas_settings and
+ md.canvas_settings.canvas_surfaces.active and
+ md.canvas_settings.canvas_surfaces.active.is_cache_user and
+ (rd.engine in cls.COMPAT_ENGINES))
+
+ def draw(self, context):
+ surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
+ cache = surface.point_cache
+
+ point_cache_ui(self, context, cache, (cache.is_baked is False), 'DYNAMIC_PAINT')
+
+
class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Source"
COMPAT_ENGINES = {'BLENDER_RENDER'}
@@ -410,6 +436,16 @@ class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
col = split.column()
col.prop(brush, "paint_source")
+ if brush.paint_source == 'PARTICLE_SYSTEM':
+ col.prop_search(brush, "particle_system", ob, "particle_systems", text="")
+ if brush.particle_system:
+ col.label(text="Particle effect:")
+ sub = col.column()
+ sub.active = not brush.use_particle_radius
+ sub.prop(brush, "solid_radius", text="Solid Radius")
+ col.prop(brush, "use_particle_radius", text="Use Particle's Radius")
+ col.prop(brush, "smooth_radius", text="Smooth radius")
+
if brush.paint_source in {'DISTANCE', 'VOLUME_DISTANCE', 'POINT'}:
col.prop(brush, "paint_distance", text="Paint Distance")
split = layout.row().split(percentage=0.4)
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index 2be61e362cc..ee9135b9dbf 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -21,6 +21,7 @@ import bpy
from bpy.types import Panel
from bl_ui.properties_physics_common import (
+ point_cache_ui,
effector_weights_ui,
)
@@ -58,6 +59,8 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
split = layout.split()
+ split.enabled = not domain.point_cache.is_baked
+
col = split.column()
col.label(text="Resolution:")
col.prop(domain, "resolution_max", text="Divisions")
@@ -88,7 +91,14 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
col = split.column()
col.label(text="Flow Source:")
col.prop(flow, "smoke_flow_source", expand=False, text="")
- if flow.smoke_flow_source == 'MESH':
+ if flow.smoke_flow_source == 'PARTICLES':
+ col.label(text="Particle System:")
+ col.prop_search(flow, "particle_system", ob, "particle_systems", text="")
+ col.prop(flow, "use_particle_size", text="Set Size")
+ sub = col.column()
+ sub.active = flow.use_particle_size
+ sub.prop(flow, "particle_size")
+ else:
col.prop(flow, "surface_distance")
col.prop(flow, "volume_density")
@@ -173,6 +183,7 @@ class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
domain = context.smoke.domain_settings
split = layout.split()
+ split.enabled = not domain.point_cache.is_baked
col = split.column(align=True)
col.label(text="Reaction:")
@@ -209,6 +220,7 @@ class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
layout.active = domain.use_adaptive_domain
split = layout.split()
+ split.enabled = (not domain.point_cache.is_baked)
col = split.column(align=True)
col.label(text="Resolution:")
@@ -244,6 +256,7 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
layout.active = md.use_high_resolution
split = layout.split()
+ split.enabled = not md.point_cache.is_baked
col = split.column()
col.label(text="Resolution:")
@@ -302,17 +315,27 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
- if not bpy.app.build_options.openvdb:
- layout.label("Built without OpenVDB support")
- return
-
domain = context.smoke.domain_settings
-
- layout.label(text="Compression:")
- layout.prop(domain, "openvdb_cache_compress_type", expand=True)
- row = layout.row()
- row.label("Data Depth:")
- row.prop(domain, "data_depth", expand=True, text="Data Depth")
+ cache_file_format = domain.cache_file_format
+
+ layout.prop(domain, "cache_file_format")
+
+ if cache_file_format == 'POINTCACHE':
+ layout.label(text="Compression:")
+ layout.prop(domain, "point_cache_compress_type", expand=True)
+ elif cache_file_format == 'OPENVDB':
+ if not bpy.app.build_options.openvdb:
+ layout.label("Built without OpenVDB support")
+ return
+
+ layout.label(text="Compression:")
+ layout.prop(domain, "openvdb_cache_compress_type", expand=True)
+ row = layout.row()
+ row.label("Data Depth:")
+ row.prop(domain, "data_depth", expand=True, text="Data Depth")
+
+ cache = domain.point_cache
+ point_cache_ui(self, context, cache, (cache.is_baked is False), 'SMOKE')
class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py
index e873bb40013..a458af739f2 100644
--- a/release/scripts/startup/bl_ui/properties_physics_softbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py
@@ -21,13 +21,13 @@ import bpy
from bpy.types import Panel
from bl_ui.properties_physics_common import (
+ point_cache_ui,
effector_weights_ui,
)
def softbody_panel_enabled(md):
- return True
- #return (md.point_cache.is_baked is False)
+ return (md.point_cache.is_baked is False)
class PhysicButtonsPanel:
@@ -71,6 +71,16 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel):
layout.prop(softbody, "collision_group")
+class PHYSICS_PT_softbody_cache(PhysicButtonsPanel, Panel):
+ bl_label = "Soft Body Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ md = context.soft_body
+ point_cache_ui(self, context, md.point_cache, softbody_panel_enabled(md), 'SOFTBODY')
+
+
class PHYSICS_PT_softbody_goal(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Goal"
bl_options = {'DEFAULT_CLOSED'}
diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py
index 7b7e2367a42..d6253ec7fbc 100644
--- a/release/scripts/startup/bl_ui/properties_scene.py
+++ b/release/scripts/startup/bl_ui/properties_scene.py
@@ -27,6 +27,7 @@ from bpy.types import (
from rna_prop_ui import PropertyPanel
from bl_ui.properties_physics_common import (
+ point_cache_ui,
effector_weights_ui,
)
@@ -370,6 +371,24 @@ class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
col.prop(rbw, "solver_iterations", text="Solver Iterations")
+class SCENE_PT_rigid_body_cache(SceneButtonsPanel, Panel):
+ bl_label = "Rigid Body Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ scene = context.scene
+ return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ scene = context.scene
+ rbw = scene.rigidbody_world
+
+ point_cache_ui(self, context, rbw.point_cache, rbw.point_cache.is_baked is False and rbw.enabled, 'RIGID_BODY')
+
+
class SCENE_PT_rigid_body_field_weights(SceneButtonsPanel, Panel):
bl_label = "Rigid Body Field Weights"
bl_options = {'DEFAULT_CLOSED'}
@@ -408,10 +427,12 @@ class SCENE_PT_simplify(SceneButtonsPanel, Panel):
col = split.column()
col.label(text="Viewport:")
col.prop(rd, "simplify_subdivision", text="Subdivision")
+ col.prop(rd, "simplify_child_particles", text="Child Particles")
col = split.column()
col.label(text="Render:")
col.prop(rd, "simplify_subdivision_render", text="Subdivision")
+ col.prop(rd, "simplify_child_particles_render", text="Child Particles")
col.prop(rd, "simplify_shadow_samples", text="Shadow Samples")
col.prop(rd, "simplify_ao_sss", text="AO and SSS")
col.prop(rd, "use_simplify_triangulate")
diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py
index 9bbef2ebdaf..caf19a9e469 100644
--- a/release/scripts/startup/bl_ui/properties_texture.py
+++ b/release/scripts/startup/bl_ui/properties_texture.py
@@ -26,6 +26,7 @@ from bpy.types import (
Lamp,
Material,
Object,
+ ParticleSettings,
Texture,
World,
)
@@ -100,6 +101,9 @@ def context_tex_datablock(context):
if idblock:
return idblock
+ if context.particle_system:
+ idblock = context.particle_system.settings
+
return idblock
@@ -138,6 +142,8 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, Panel):
context.lamp or
context.texture or
context.line_style or
+ context.particle_system or
+ isinstance(context.space_data.pin_id, ParticleSettings) or
context.texture_user) and
(engine in cls.COMPAT_ENGINES))
@@ -805,7 +811,18 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
split = layout.split()
col = split.column()
- if pd.point_source == 'OBJECT':
+ if pd.point_source == 'PARTICLE_SYSTEM':
+ col.label(text="Object:")
+ col.prop(pd, "object", text="")
+
+ sub = col.column()
+ sub.enabled = bool(pd.object)
+ if pd.object:
+ sub.label(text="System:")
+ sub.prop_search(pd, "particle_system", pd.object, "particle_systems", text="")
+ sub.label(text="Cache:")
+ sub.prop(pd, "particle_cache_space", text="")
+ else:
col.label(text="Object:")
col.prop(pd, "object", text="")
col.label(text="Cache:")
@@ -814,7 +831,13 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
col.separator()
col.label(text="Color Source:")
- if pd.point_source == 'OBJECT':
+ if pd.point_source == 'PARTICLE_SYSTEM':
+ col.prop(pd, "particle_color_source", text="")
+ if pd.particle_color_source in {'PARTICLE_SPEED', 'PARTICLE_VELOCITY'}:
+ col.prop(pd, "speed_scale")
+ if pd.particle_color_source in {'PARTICLE_SPEED', 'PARTICLE_AGE'}:
+ layout.template_color_ramp(pd, "color_ramp", expand=True)
+ else:
col.prop(pd, "vertex_color_source", text="")
if pd.vertex_color_source == 'VERTEX_COLOR':
if pd.object and pd.object.data:
@@ -831,6 +854,8 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
col.prop(pd, "falloff", text="")
if pd.falloff == 'SOFT':
col.prop(pd, "falloff_soft")
+ if pd.falloff == 'PARTICLE_VELOCITY':
+ col.prop(pd, "falloff_speed_scale")
col.prop(pd, "use_falloff_curve")
@@ -1122,6 +1147,35 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
col = split.column()
factor_but(col, "use_map_zenith_up", "zenith_up_factor", "Zenith Up")
factor_but(col, "use_map_zenith_down", "zenith_down_factor", "Zenith Down")
+ elif isinstance(idblock, ParticleSettings):
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="General:")
+ factor_but(col, "use_map_time", "time_factor", "Time")
+ factor_but(col, "use_map_life", "life_factor", "Lifetime")
+ factor_but(col, "use_map_density", "density_factor", "Density")
+ factor_but(col, "use_map_size", "size_factor", "Size")
+
+ col = split.column()
+ col.label(text="Physics:")
+ factor_but(col, "use_map_velocity", "velocity_factor", "Velocity")
+ factor_but(col, "use_map_damp", "damp_factor", "Damp")
+ factor_but(col, "use_map_gravity", "gravity_factor", "Gravity")
+ factor_but(col, "use_map_field", "field_factor", "Force Fields")
+
+ layout.label(text="Hair:")
+
+ split = layout.split()
+
+ col = split.column()
+ factor_but(col, "use_map_length", "length_factor", "Length")
+ factor_but(col, "use_map_clump", "clump_factor", "Clump")
+
+ col = split.column()
+ factor_but(col, "use_map_kink_amp", "kink_amp_factor", "Kink Amplitude")
+ factor_but(col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency")
+ factor_but(col, "use_map_rough", "rough_factor", "Rough")
elif isinstance(idblock, FreestyleLineStyle):
split = layout.split()
@@ -1133,17 +1187,18 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
layout.separator()
- split = layout.split()
+ if not isinstance(idblock, ParticleSettings):
+ split = layout.split()
- col = split.column()
- col.prop(tex, "blend_type", text="Blend")
- col.prop(tex, "use_rgb_to_intensity")
- # color is used on gray-scale textures even when use_rgb_to_intensity is disabled.
- col.prop(tex, "color", text="")
+ col = split.column()
+ col.prop(tex, "blend_type", text="Blend")
+ col.prop(tex, "use_rgb_to_intensity")
+ # color is used on gray-scale textures even when use_rgb_to_intensity is disabled.
+ col.prop(tex, "color", text="")
- col = split.column()
- col.prop(tex, "invert", text="Negative")
- col.prop(tex, "use_stencil")
+ col = split.column()
+ col.prop(tex, "invert", text="Negative")
+ col.prop(tex, "use_stencil")
if isinstance(idblock, Material) or isinstance(idblock, World):
col.prop(tex, "default_value", text="DVar", slider=True)
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index be9752a463e..4d365c8dc08 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -92,6 +92,8 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
row.prop(dopesheet, "show_lattices", text="")
if bpy.data.armatures:
row.prop(dopesheet, "show_armatures", text="")
+ if bpy.data.particles:
+ row.prop(dopesheet, "show_particles", text="")
if bpy.data.speakers:
row.prop(dopesheet, "show_speakers", text="")
if bpy.data.linestyles:
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 894be977822..508e62e4f56 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -171,6 +171,7 @@ class TIME_MT_cache(Menu):
col = layout.column()
col.enabled = st.show_cache
col.prop(st, "cache_softbody")
+ col.prop(st, "cache_particles")
col.prop(st, "cache_cloth")
col.prop(st, "cache_smoke")
col.prop(st, "cache_dynamicpaint")
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 94d7777ad1b..e5b6a94c1ab 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -371,6 +371,7 @@ class USERPREF_PT_edit(Panel):
col.prop(edit, "use_duplicate_texture", text="Texture")
#col.prop(edit, "use_duplicate_fcurve", text="F-Curve")
col.prop(edit, "use_duplicate_action", text="Action")
+ col.prop(edit, "use_duplicate_particle", text="Particle")
class USERPREF_PT_system(Panel):
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 2739248391a..2798a2fd0d0 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -49,9 +49,12 @@ class VIEW3D_HT_header(Header):
if obj:
mode = obj.mode
+ # Particle edit
+ if mode == 'PARTICLE_EDIT':
+ row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True)
# Occlude geometry
- if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'EDIT' and obj.type == 'MESH')) or
+ if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'PARTICLE_EDIT' or (mode == 'EDIT' and obj.type == 'MESH'))) or
(mode == 'WEIGHT_PAINT')):
row.prop(view, "use_occlude_geometry", text="")
@@ -61,7 +64,7 @@ class VIEW3D_HT_header(Header):
row.prop(toolsettings, "proportional_edit", icon_only=True)
if toolsettings.proportional_edit != 'DISABLED':
row.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
- elif mode == 'EDIT':
+ elif mode in {'EDIT', 'PARTICLE_EDIT'}:
row = layout.row(align=True)
row.prop(toolsettings, "proportional_edit", icon_only=True)
if toolsettings.proportional_edit != 'DISABLED':
@@ -700,6 +703,35 @@ class VIEW3D_MT_select_pose(Menu):
layout.operator("object.select_pattern", text="Select Pattern...")
+class VIEW3D_MT_select_particle(Menu):
+ bl_label = "Select"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("view3d.select_border")
+
+ layout.separator()
+
+ layout.operator("particle.select_all").action = 'TOGGLE'
+ layout.operator("particle.select_linked")
+ layout.operator("particle.select_all", text="Inverse").action = 'INVERT'
+
+ layout.separator()
+
+ layout.operator("particle.select_more")
+ layout.operator("particle.select_less")
+
+ layout.separator()
+
+ layout.operator("particle.select_random")
+
+ layout.separator()
+
+ layout.operator("particle.select_roots", text="Roots")
+ layout.operator("particle.select_tips", text="Tips")
+
+
class VIEW3D_MT_edit_mesh_select_similar(Menu):
bl_label = "Select Similar"
@@ -1890,8 +1922,87 @@ class VIEW3D_MT_hide_mask(Menu):
props = layout.operator("paint.mask_lasso_gesture", text="Lasso Mask")
+# ********** Particle menu **********
+
+
+class VIEW3D_MT_particle(Menu):
+ bl_label = "Particle"
+
+ def draw(self, context):
+ layout = self.layout
+
+ particle_edit = context.tool_settings.particle_edit
+
+ layout.operator("ed.undo")
+ layout.operator("ed.redo")
+ layout.operator("ed.undo_history")
+
+ layout.separator()
+
+ layout.operator("particle.mirror")
+
+ layout.separator()
+
+ layout.operator("particle.remove_doubles")
+ layout.operator("particle.delete")
+
+ if particle_edit.select_mode == 'POINT':
+ layout.operator("particle.subdivide")
+
layout.operator("particle.unify_length")
+ layout.operator("particle.rekey")
+ layout.operator("particle.weight_set")
+
+ layout.separator()
+
+ layout.menu("VIEW3D_MT_particle_showhide")
+
+
+class VIEW3D_MT_particle_specials(Menu):
+ bl_label = "Specials"
+
+ def draw(self, context):
+ layout = self.layout
+
+ particle_edit = context.tool_settings.particle_edit
+
+ layout.operator("particle.rekey")
+ layout.operator("particle.delete")
+ layout.operator("particle.remove_doubles")
layout.operator("particle.unify_length")
+
+ if particle_edit.select_mode == 'POINT':
+ layout.operator("particle.subdivide")
+
+ layout.operator("particle.weight_set")
+ layout.separator()
+
+ layout.operator("particle.mirror")
+
+ if particle_edit.select_mode == 'POINT':
+ layout.separator()
+ layout.operator("particle.select_roots")
+ layout.operator("particle.select_tips")
+
+ layout.separator()
+
+ layout.operator("particle.select_random")
+
+ layout.separator()
+
+ layout.operator("particle.select_more")
+ layout.operator("particle.select_less")
+
+ layout.separator()
+
+ layout.operator("particle.select_all").action = 'TOGGLE'
+ layout.operator("particle.select_linked")
+ layout.operator("particle.select_all", text="Inverse").action = 'INVERT'
+
+
+class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
+ _operator_name = "particle"
+
# ********** Pose Menu **********
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 1671e26e8d4..3eb76a3b0f9 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -943,12 +943,37 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
settings = self.paint_settings(context)
brush = settings.brush
- col = layout.split().column()
- col.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8)
+ if not context.particle_edit_object:
+ col = layout.split().column()
+ col.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8)
+
+ # Particle Mode #
+ if context.particle_edit_object:
+ tool = settings.tool
+
+ layout.column().prop(settings, "tool", expand=True)
+
+ if tool != 'NONE':
+ col = layout.column()
+ col.prop(brush, "size", slider=True)
+ if tool != 'ADD':
+ col.prop(brush, "strength", slider=True)
+
+ if tool == 'ADD':
+ col.prop(brush, "count")
+ col = layout.column()
+ col.prop(settings, "use_default_interpolate")
+ col.prop(brush, "steps", slider=True)
+ col.prop(settings, "default_key_count", slider=True)
+ elif tool == 'LENGTH':
+ layout.prop(brush, "length_mode", expand=True)
+ elif tool == 'PUFF':
+ layout.prop(brush, "puff_mode", expand=True)
+ layout.prop(brush, "use_puff_volume")
# Sculpt Mode #
- if context.sculpt_object and brush:
+ elif context.sculpt_object and brush:
capabilities = brush.sculpt_capabilities
col = layout.column()
@@ -1633,7 +1658,7 @@ class VIEW3D_PT_tools_brush_appearance(Panel, View3DPaintPanel):
@classmethod
def poll(cls, context):
settings = cls.paint_settings(context)
- return (settings is not None)
+ return (settings is not None) and (not isinstance(settings, bpy.types.ParticleEdit))
def draw(self, context):
layout = self.layout
@@ -1856,6 +1881,78 @@ class VIEW3D_MT_tools_projectpaint_stencil(Menu):
props.value = i
+class VIEW3D_PT_tools_particlemode(View3DPanel, Panel):
+ """Default tools for particle mode"""
+ bl_context = "particlemode"
+ bl_label = "Options"
+ bl_category = "Tools"
+
+ def draw(self, context):
+ layout = self.layout
+
+ pe = context.tool_settings.particle_edit
+ ob = pe.object
+
+ layout.prop(pe, "type", text="")
+
+ ptcache = None
+
+ if pe.type == 'PARTICLES':
+ if ob.particle_systems:
+ if len(ob.particle_systems) > 1:
+ layout.template_list("UI_UL_list", "particle_systems", ob, "particle_systems",
+ ob.particle_systems, "active_index", rows=2, maxrows=3)
+
+ ptcache = ob.particle_systems.active.point_cache
+ else:
+ for md in ob.modifiers:
+ if md.type == pe.type:
+ ptcache = md.point_cache
+
+ if ptcache and len(ptcache.point_caches) > 1:
+ layout.template_list("UI_UL_list", "particles_point_caches", ptcache, "point_caches",
+ ptcache.point_caches, "active_index", rows=2, maxrows=3)
+
+ if not pe.is_editable:
+ layout.label(text="Point cache must be baked")
+ layout.label(text="in memory to enable editing!")
+
+ col = layout.column(align=True)
+ if pe.is_hair:
+ col.active = pe.is_editable
+ col.prop(pe, "use_emitter_deflect", text="Deflect emitter")
+ sub = col.row(align=True)
+ sub.active = pe.use_emitter_deflect
+ sub.prop(pe, "emitter_distance", text="Distance")
+
+ col = layout.column(align=True)
+ col.active = pe.is_editable
+ col.label(text="Keep:")
+ col.prop(pe, "use_preserve_length", text="Lengths")
+ col.prop(pe, "use_preserve_root", text="Root")
+ if not pe.is_hair:
+ col.label(text="Correct:")
+ col.prop(pe, "use_auto_velocity", text="Velocity")
+ col.prop(ob.data, "use_mirror_x")
+
+ col.prop(pe, "shape_object")
+ col.operator("particle.shape_cut")
+
+ col = layout.column(align=True)
+ col.active = pe.is_editable
+ col.label(text="Draw:")
+ col.prop(pe, "draw_step", text="Path Steps")
+ if pe.is_hair:
+ col.prop(pe, "show_particles", text="Children")
+ else:
+ if pe.type == 'PARTICLES':
+ col.prop(pe, "show_particles", text="Particles")
+ col.prop(pe, "use_fade_time")
+ sub = col.row(align=True)
+ sub.active = pe.use_fade_time
+ sub.prop(pe, "fade_frames", slider=True)
+
+
# Grease Pencil drawing tools
class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
bl_space_type = 'VIEW_3D'