Files
blender/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
David Vogel 820ca419e0 Add compound shape for rigid body simulation
This patch adds a new compound shape entry to the shape selection
dropdown. It also corrects wrong inertia calculation for convex hulls,
that resulted in strange behavior for small objects.

The compound shape take the collision shapes from its object children
and combines them. This makes it possible to create concave shapes from
primitive shapes. Using this instead of the mesh collision shape is
often many times faster.

Reviewed By: Sergey, Sebastian Parborg

Differential Revision: http://developer.blender.org/D5797
2020-07-30 18:53:35 +02:00

314 lines
10 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>
from bpy.types import (
Panel,
)
def rigid_body_warning(layout, text):
row = layout.row(align=True)
row.alignment = 'RIGHT'
row.label(text=text, icon='ERROR')
class PHYSICS_PT_rigidbody_panel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "physics"
class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Rigid Body"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
parent = ob.parent
rbo = ob.rigid_body
if rbo is None:
rigid_body_warning(layout, "Object does not have a Rigid Body")
return
if parent is not None and parent.rigid_body is not None:
if parent.rigid_body.collision_shape == 'COMPOUND':
row = layout.row(align=True)
row.alignment = 'RIGHT'
row.label(text="This object is part of a compound shape", icon='INFO')
else:
rigid_body_warning(layout, "Rigid Body can't be child of a non compound Rigid Body")
return
if parent is None or parent.rigid_body is None:
layout.prop(rbo, "type", text="Type")
class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Settings"
bl_parent_id = 'PHYSICS_PT_rigid_body'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None:
return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
rbo = ob.rigid_body
if rbo is None:
rigid_body_warning(layout, "Object does not have a Rigid Body")
return
col = layout.column()
if rbo.type == 'ACTIVE':
col.prop(rbo, "mass")
col.prop(rbo, "enabled", text="Dynamic")
col.prop(rbo, "kinematic", text="Animated")
class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Collisions"
bl_parent_id = 'PHYSICS_PT_rigid_body'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND':
return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
ob = context.object
parent = ob.parent
rbo = ob.rigid_body
layout.use_property_split = True
layout.prop(rbo, "collision_shape", text="Shape")
if rbo.collision_shape == 'COMPOUND':
if parent is not None and parent.rigid_body is not None and parent.rigid_body.collision_shape == 'COMPOUND':
rigid_body_warning(layout, "Sub compound shapes are not allowed")
else:
found = False
for child in ob.children:
if child.rigid_body is not None:
found = True
break
if not found:
rigid_body_warning(layout, "There are no child rigid bodies")
if rbo.collision_shape in {'MESH', 'CONVEX_HULL'}:
layout.prop(rbo, "mesh_source", text="Source")
if rbo.collision_shape == 'MESH' and rbo.mesh_source == 'DEFORM':
layout.prop(rbo, "use_deform", text="Deforming")
class PHYSICS_PT_rigid_body_collisions_surface(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Surface Response"
bl_parent_id = 'PHYSICS_PT_rigid_body_collisions'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None:
return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
ob = context.object
rbo = ob.rigid_body
col = flow.column()
col.prop(rbo, "friction")
col = flow.column()
col.prop(rbo, "restitution", text="Bounciness")
class PHYSICS_PT_rigid_body_collisions_sensitivity(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Sensitivity"
bl_parent_id = 'PHYSICS_PT_rigid_body_collisions'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND':
return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
rbo = ob.rigid_body
if rbo.collision_shape in {'MESH', 'CONE'}:
col = layout.column()
col.prop(rbo, "collision_margin", text="Margin")
else:
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
col.prop(rbo, "use_margin")
col = flow.column()
col.active = rbo.use_margin
col.prop(rbo, "collision_margin", text="Margin")
class PHYSICS_PT_rigid_body_collisions_collections(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Collections"
bl_parent_id = 'PHYSICS_PT_rigid_body_collisions'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None:
return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
ob = context.object
rbo = ob.rigid_body
layout.prop(rbo, "collision_collections", text="")
class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Dynamics"
bl_parent_id = 'PHYSICS_PT_rigid_body'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
if obj.parent is not None and obj.parent.rigid_body is not None:
return False
return (obj and obj.rigid_body and obj.rigid_body.type == 'ACTIVE'
and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
ob = context.object
rbo = ob.rigid_body
# col = layout.column(align=True)
# col.label(text="Activation:")
# XXX: settings such as activate on collison/etc.
col = flow.column()
col.prop(rbo, "linear_damping", text="Damping Translation")
col = flow.column()
col.prop(rbo, "angular_damping", text="Rotation")
class PHYSICS_PT_rigid_body_dynamics_deactivation(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Deactivation"
bl_parent_id = 'PHYSICS_PT_rigid_body_dynamics'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
obj = context.object
return (obj and obj.rigid_body
and obj.rigid_body.type == 'ACTIVE'
and (context.engine in cls.COMPAT_ENGINES))
def draw_header(self, context):
ob = context.object
rbo = ob.rigid_body
self.layout.prop(rbo, "use_deactivation", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
ob = context.object
rbo = ob.rigid_body
layout.active = rbo.use_deactivation
col = flow.column()
col.prop(rbo, "use_start_deactivated")
col = flow.column()
col.prop(rbo, "deactivate_linear_velocity", text="Velocity Linear")
col.prop(rbo, "deactivate_angular_velocity", text="Angular")
# TODO: other params such as time?
classes = (
PHYSICS_PT_rigid_body,
PHYSICS_PT_rigid_body_settings,
PHYSICS_PT_rigid_body_collisions,
PHYSICS_PT_rigid_body_collisions_surface,
PHYSICS_PT_rigid_body_collisions_sensitivity,
PHYSICS_PT_rigid_body_collisions_collections,
PHYSICS_PT_rigid_body_dynamics,
PHYSICS_PT_rigid_body_dynamics_deactivation,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)