This separates cloth stiffness and damping forces into tension, compression, and shearing components, allowing more control over the cloth behaviour. This also adds a bending model selector (although the new bending model itself is not implemented in this commit). This is because some of the features implemented here only make sense within the new bending model, while the old model is kept for compatibility. This commit makes non-breaking changes, and thus maintains full compatibility with existing simulations. Reviewed By: brecht Differential Revision: http://developer.blender.org/D3655
354 lines
11 KiB
Python
354 lines
11 KiB
Python
# ##### 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,
|
|
)
|
|
from bl_operators.presets import PresetMenu
|
|
|
|
from .properties_physics_common import (
|
|
point_cache_ui,
|
|
effector_weights_ui,
|
|
)
|
|
|
|
|
|
def cloth_panel_enabled(md):
|
|
return md.point_cache.is_baked is False
|
|
|
|
|
|
class CLOTH_PT_presets(PresetMenu):
|
|
bl_label = "Cloth Presets"
|
|
preset_subdir = "cloth"
|
|
preset_operator = "script.execute_preset"
|
|
preset_add_operator = "cloth.preset_add"
|
|
|
|
|
|
class PhysicButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "physics"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.cloth)
|
|
|
|
|
|
class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
|
|
bl_label = "Cloth"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header_preset(self, context):
|
|
CLOTH_PT_presets.draw_panel_header(self.layout)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
md = context.cloth
|
|
ob = context.object
|
|
cloth = md.settings
|
|
|
|
layout.active = cloth_panel_enabled(md)
|
|
|
|
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "quality", text="Quality Steps")
|
|
col.prop(cloth, "time_scale", text="Speed Multiplier")
|
|
col.prop(cloth, "bending_model")
|
|
|
|
col.separator()
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "mass", text="Material Mass")
|
|
col.prop(cloth, "air_damping", text="Air")
|
|
col.prop(cloth, "vel_damping", text="Velocity")
|
|
|
|
col.separator()
|
|
|
|
col = flow.column()
|
|
if cloth.bending_model == 'ANGULAR':
|
|
col.prop(cloth, "tension_stiffness", text="Stiffness Tension")
|
|
col.prop(cloth, "compression_stiffness", text="Compression")
|
|
else:
|
|
col.prop(cloth, "tension_stiffness", text="Stiffness Structural")
|
|
|
|
col.prop(cloth, "shear_stiffness", text="Shear")
|
|
col.prop(cloth, "bending_stiffness", text="Bending")
|
|
|
|
col.separator()
|
|
|
|
col = flow.column()
|
|
if cloth.bending_model == 'ANGULAR':
|
|
col.prop(cloth, "tension_damping", text="Damping Tension")
|
|
col.prop(cloth, "compression_damping", text="Compression")
|
|
else:
|
|
col.prop(cloth, "tension_damping", text="Damping Structural")
|
|
|
|
col.prop(cloth, "shear_damping", text="Shear")
|
|
col.prop(cloth, "bending_damping", text="Bending")
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "use_dynamic_mesh", text="Dynamic Mesh")
|
|
|
|
key = ob.data.shape_keys
|
|
|
|
if key:
|
|
row = col.row(align=True)
|
|
row.active = not cloth.use_dynamic_mesh
|
|
row.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="Rest Shape Key")
|
|
|
|
|
|
class PHYSICS_PT_cloth_pinning(PhysicButtonsPanel, Panel):
|
|
bl_label = "Pinning"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header(self, context):
|
|
md = context.cloth
|
|
cloth = md.settings
|
|
|
|
self.layout.active = cloth_panel_enabled(md)
|
|
self.layout.prop(cloth, "use_pin_cloth", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
md = context.cloth
|
|
ob = context.object
|
|
cloth = md.settings
|
|
|
|
layout.active = cloth_panel_enabled(md) and cloth.use_pin_cloth
|
|
|
|
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
|
|
col.prop_search(cloth, "vertex_group_mass", ob, "vertex_groups", text="Mass Group")
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "pin_stiffness", text="Stiffness")
|
|
|
|
# Disabled for now.
|
|
"""
|
|
if cloth.vertex_group_mass:
|
|
col = flow.column()
|
|
col.prop(cloth, "goal_default", text="Goal Default")
|
|
col.prop(cloth, "goal_spring", text="Stiffness")
|
|
col.prop(cloth, "goal_friction", text="Friction")
|
|
"""
|
|
|
|
|
|
class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, Panel):
|
|
bl_label = "Cache"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
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 = "Collision"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header(self, context):
|
|
cloth = context.cloth.collision_settings
|
|
|
|
self.layout.active = cloth_panel_enabled(context.cloth)
|
|
self.layout.prop(cloth, "use_collision", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cloth = context.cloth.collision_settings
|
|
md = context.cloth
|
|
|
|
layout.active = cloth.use_collision and cloth_panel_enabled(md)
|
|
|
|
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "collision_quality", text="Quality")
|
|
col.prop(cloth, "distance_min", slider=True, text="Distance")
|
|
col.prop(cloth, "repel_force", slider=True, text="Repel")
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "distance_repel", slider=True, text="Repel Distance")
|
|
col.prop(cloth, "friction")
|
|
col.prop(cloth, "group")
|
|
|
|
|
|
class PHYSICS_PT_cloth_self_collision(PhysicButtonsPanel, Panel):
|
|
bl_label = "Self Collision"
|
|
bl_parent_id = 'PHYSICS_PT_cloth_collision'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header(self, context):
|
|
cloth = context.cloth.collision_settings
|
|
|
|
self.layout.active = cloth_panel_enabled(context.cloth) and cloth.use_self_collision
|
|
self.layout.prop(cloth, "use_self_collision", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cloth = context.cloth.collision_settings
|
|
md = context.cloth
|
|
ob = context.object
|
|
|
|
layout.active = cloth.use_collision and cloth_panel_enabled(md) and cloth.use_self_collision
|
|
|
|
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "self_collision_quality", text="Quality")
|
|
col.prop(cloth, "self_distance_min", slider=True, text="Distance")
|
|
|
|
col = flow.column()
|
|
col.prop_search(cloth, "vertex_group_self_collisions", ob, "vertex_groups", text="Vertex Group")
|
|
|
|
|
|
class PHYSICS_PT_cloth_stiffness(PhysicButtonsPanel, Panel):
|
|
bl_label = "Stiffness Scaling"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header(self, context):
|
|
cloth = context.cloth.settings
|
|
|
|
self.layout.active = cloth_panel_enabled(context.cloth)
|
|
self.layout.prop(cloth, "use_stiffness_scale", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
md = context.cloth
|
|
ob = context.object
|
|
cloth = context.cloth.settings
|
|
|
|
layout.active = (cloth.use_stiffness_scale and cloth_panel_enabled(md))
|
|
|
|
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
col.prop_search(
|
|
cloth, "vertex_group_structural_stiffness", ob, "vertex_groups",
|
|
text="Structural Group"
|
|
)
|
|
col.prop(cloth, "tension_stiffness_max", text="Max Tension")
|
|
col.prop(cloth, "compression_stiffness_max", text="Compression")
|
|
|
|
col.separator()
|
|
|
|
col = flow.column()
|
|
col.prop_search(
|
|
cloth, "vertex_group_shear_stiffness", ob, "vertex_groups",
|
|
text="Shear Group"
|
|
)
|
|
col.prop(cloth, "shear_stiffness_max", text="Max")
|
|
|
|
col.separator()
|
|
|
|
col = flow.column()
|
|
col.prop_search(
|
|
cloth, "vertex_group_bending", ob, "vertex_groups",
|
|
text="Bending Group"
|
|
)
|
|
col.prop(cloth, "bending_stiffness_max", text="Max")
|
|
|
|
|
|
class PHYSICS_PT_cloth_sewing(PhysicButtonsPanel, Panel):
|
|
bl_label = "Sewing Springs"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw_header(self, context):
|
|
cloth = context.cloth.settings
|
|
|
|
self.layout.active = cloth_panel_enabled(context.cloth)
|
|
self.layout.prop(cloth, "use_sewing_springs", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
md = context.cloth
|
|
ob = context.object
|
|
cloth = context.cloth.settings
|
|
|
|
layout.active = (cloth.use_sewing_springs and cloth_panel_enabled(md))
|
|
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
|
|
|
|
col = flow.column()
|
|
col.prop(cloth, "sewing_force_max", text="Sewing Force")
|
|
|
|
col.separator()
|
|
|
|
col = col.column()
|
|
col.prop_search(cloth, "vertex_group_shrink", ob, "vertex_groups", text="Shrinking Group")
|
|
|
|
col = flow.column(align=True)
|
|
col.prop(cloth, "shrink_min", text="Min")
|
|
col.prop(cloth, "shrink_max", text="Max")
|
|
|
|
|
|
class PHYSICS_PT_cloth_field_weights(PhysicButtonsPanel, Panel):
|
|
bl_label = "Field Weights"
|
|
bl_parent_id = 'PHYSICS_PT_cloth'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
|
|
|
def draw(self, context):
|
|
cloth = context.cloth.settings
|
|
effector_weights_ui(self, context, cloth.effector_weights, 'CLOTH')
|
|
|
|
|
|
classes = (
|
|
CLOTH_PT_presets,
|
|
PHYSICS_PT_cloth,
|
|
PHYSICS_PT_cloth_cache,
|
|
PHYSICS_PT_cloth_collision,
|
|
PHYSICS_PT_cloth_self_collision,
|
|
PHYSICS_PT_cloth_pinning,
|
|
PHYSICS_PT_cloth_stiffness,
|
|
PHYSICS_PT_cloth_sewing,
|
|
PHYSICS_PT_cloth_field_weights,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|