
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
314 lines
10 KiB
Python
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)
|