1# ##### BEGIN GPL LICENSE BLOCK ##### 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software Foundation, 15# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16# 17# ##### END GPL LICENSE BLOCK ##### 18 19# <pep8 compliant> 20 21from bpy.types import ( 22 Panel, 23) 24from bl_ui.properties_physics_common import ( 25 basic_force_field_settings_ui, 26 basic_force_field_falloff_ui, 27) 28 29 30class PhysicButtonsPanel: 31 bl_space_type = 'PROPERTIES' 32 bl_region_type = 'WINDOW' 33 bl_context = "physics" 34 35 @staticmethod 36 def poll_force_field(context): 37 ob = context.object 38 return (ob and (ob.field) and (ob.field.type != 'NONE')) 39 40 @staticmethod 41 def poll_collision(context): 42 ob = context.object 43 return (ob and ob.type == 'MESH') and (context.collision) 44 45 46class PHYSICS_PT_field(PhysicButtonsPanel, Panel): 47 bl_label = "Force Fields" 48 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 49 50 @classmethod 51 def poll(cls, context): 52 if not PhysicButtonsPanel.poll_force_field(context): 53 return False 54 55 return (context.engine in cls.COMPAT_ENGINES) 56 57 def draw(self, context): 58 layout = self.layout 59 layout.use_property_split = True 60 61 ob = context.object 62 field = ob.field 63 64 layout.prop(field, "type") 65 66 67class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): 68 bl_label = "Settings" 69 bl_parent_id = 'PHYSICS_PT_field' 70 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 71 72 @classmethod 73 def poll(cls, context): 74 if not PhysicButtonsPanel.poll_force_field(context): 75 return False 76 77 return (context.engine in cls.COMPAT_ENGINES) 78 79 def draw(self, context): 80 layout = self.layout 81 layout.use_property_split = True 82 83 ob = context.object 84 field = ob.field 85 86 if field.type not in {'NONE', 'GUIDE', 'TEXTURE'}: 87 layout.prop(field, "shape", text="Shape") 88 89 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 90 91 if field.type == 'NONE': 92 return # nothing to draw. 93 94 elif field.type == 'GUIDE': 95 col = flow.column() 96 col.prop(field, "guide_minimum") 97 col.prop(field, "guide_free") 98 col.prop(field, "falloff_power") 99 col.prop(field, "use_guide_path_add") 100 col.prop(field, "use_guide_path_weight") 101 102 col.separator() 103 104 col = flow.column() 105 col.prop(field, "guide_clump_amount", text="Clumping amount") 106 col.prop(field, "guide_clump_shape") 107 col.prop(field, "use_max_distance") 108 109 sub = col.column() 110 sub.active = field.use_max_distance 111 sub.prop(field, "distance_max") 112 113 elif field.type == 'TEXTURE': 114 col = flow.column() 115 col.prop(field, "texture_mode") 116 117 col.separator() 118 119 col.prop(field, "strength") 120 121 col = flow.column() 122 col.prop(field, "texture_nabla") 123 col.prop(field, "use_object_coords") 124 col.prop(field, "use_2d_force") 125 126 elif field.type == 'FLUID_FLOW': 127 col = flow.column() 128 col.prop(field, "strength") 129 col.prop(field, "flow") 130 131 col = flow.column() 132 col.prop(field, "source_object") 133 col.prop(field, "use_smoke_density") 134 else: 135 del flow 136 basic_force_field_settings_ui(self, field) 137 138 139class PHYSICS_PT_field_settings_kink(PhysicButtonsPanel, Panel): 140 bl_label = "Kink" 141 bl_parent_id = 'PHYSICS_PT_field_settings' 142 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 143 144 @classmethod 145 def poll(cls, context): 146 if not PhysicButtonsPanel.poll_force_field(context): 147 return False 148 149 ob = context.object 150 return ((ob.field.type == 'GUIDE') and (context.engine in cls.COMPAT_ENGINES)) 151 152 def draw(self, context): 153 layout = self.layout 154 layout.use_property_split = True 155 156 ob = context.object 157 field = ob.field 158 159 layout.prop(field, "guide_kink_type", text="Type") 160 161 if field.guide_kink_type != 'NONE': 162 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 163 164 col = flow.column() 165 col.prop(field, "guide_kink_axis") 166 col.prop(field, "guide_kink_frequency") 167 168 col = flow.column() 169 col.prop(field, "guide_kink_shape") 170 col.prop(field, "guide_kink_amplitude") 171 172 173class PHYSICS_PT_field_settings_texture_select(PhysicButtonsPanel, Panel): 174 bl_label = "Texture" 175 bl_parent_id = 'PHYSICS_PT_field_settings' 176 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 177 178 @classmethod 179 def poll(cls, context): 180 if not PhysicButtonsPanel.poll_force_field(context): 181 return False 182 183 ob = context.object 184 return ((ob.field.type == 'TEXTURE') and (context.engine in cls.COMPAT_ENGINES)) 185 186 def draw(self, context): 187 layout = self.layout 188 189 ob = context.object 190 field = ob.field 191 192 layout.row().template_ID(field, "texture", new="texture.new") 193 194 195class PHYSICS_PT_field_falloff(PhysicButtonsPanel, Panel): 196 bl_label = "Falloff" 197 bl_parent_id = "PHYSICS_PT_field" 198 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 199 200 @classmethod 201 def poll(cls, context): 202 if not PhysicButtonsPanel.poll_force_field(context): 203 return False 204 205 ob = context.object 206 return ((ob.field.type not in {'NONE', 'GUIDE'}) and (context.engine in cls.COMPAT_ENGINES)) 207 208 def draw(self, context): 209 layout = self.layout 210 layout.use_property_split = True 211 212 ob = context.object 213 field = ob.field 214 215 layout.prop(field, "falloff_type", text="Shape") 216 217 basic_force_field_falloff_ui(self, field) 218 219 220class PHYSICS_PT_field_falloff_angular(PhysicButtonsPanel, Panel): 221 bl_label = "Angular" 222 bl_parent_id = "PHYSICS_PT_field_falloff" 223 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 224 225 @classmethod 226 def poll(cls, context): 227 if not PhysicButtonsPanel.poll_force_field(context): 228 return False 229 230 ob = context.object 231 return ((ob.field.falloff_type == 'CONE') and (context.engine in cls.COMPAT_ENGINES)) 232 233 def draw(self, context): 234 layout = self.layout 235 layout.use_property_split = True 236 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 237 238 ob = context.object 239 field = ob.field 240 241 col = flow.column() 242 col.prop(field, "radial_falloff", text="Power") 243 244 col = flow.column() 245 col.prop(field, "use_radial_min", text="Use Min Angle") 246 247 sub = col.column() 248 sub.active = field.use_radial_min 249 sub.prop(field, "radial_min", text="Min Angle") 250 251 col = flow.column() 252 col.prop(field, "use_radial_max", text="Use Max Angle") 253 254 sub = col.column() 255 sub.active = field.use_radial_max 256 sub.prop(field, "radial_max", text="Max Angle") 257 258 259class PHYSICS_PT_field_falloff_radial(PhysicButtonsPanel, Panel): 260 bl_label = "Radial" 261 bl_parent_id = "PHYSICS_PT_field_falloff" 262 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 263 264 @classmethod 265 def poll(cls, context): 266 if not PhysicButtonsPanel.poll_force_field(context): 267 return False 268 269 ob = context.object 270 return ((ob.field.falloff_type == 'TUBE') and (context.engine in cls.COMPAT_ENGINES)) 271 272 def draw(self, context): 273 layout = self.layout 274 layout.use_property_split = True 275 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 276 277 ob = context.object 278 field = ob.field 279 280 col = flow.column() 281 col.prop(field, "radial_falloff", text="Power") 282 283 col = flow.column() 284 col.prop(field, "use_radial_min", text="Use Minimum") 285 286 sub = col.column() 287 sub.active = field.use_radial_min 288 sub.prop(field, "radial_min", text="Min Distance") 289 290 col = flow.column() 291 col.prop(field, "use_radial_max", text="Use Maximum") 292 293 sub = col.column() 294 sub.active = field.use_radial_max 295 sub.prop(field, "radial_max", text="Max Distance") 296 297 298def collision_warning(layout): 299 row = layout.row(align=True) 300 row.alignment = 'RIGHT' 301 row.label(text="No collision settings available") 302 303 304class PHYSICS_PT_collision(PhysicButtonsPanel, Panel): 305 bl_label = "Collision" 306 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 307 308 @classmethod 309 def poll(cls, context): 310 if not PhysicButtonsPanel.poll_collision(context): 311 return False 312 313 return (context.engine in cls.COMPAT_ENGINES) 314 315 def draw(self, context): 316 layout = self.layout 317 layout.use_property_split = True 318 319 md = context.collision 320 coll = md.settings 321 322 if not coll: 323 collision_warning(layout) 324 return 325 326 settings = context.object.collision 327 328 layout.active = settings.use 329 330 col = layout.column() 331 col.prop(settings, "absorption", text="Field Absorption") 332 333 334class PHYSICS_PT_collision_particle(PhysicButtonsPanel, Panel): 335 bl_label = "Particle" 336 bl_parent_id = "PHYSICS_PT_collision" 337 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 338 339 @classmethod 340 def poll(cls, context): 341 if not PhysicButtonsPanel.poll_collision(context): 342 return False 343 344 return (context.engine in cls.COMPAT_ENGINES) 345 346 def draw(self, context): 347 layout = self.layout 348 349 md = context.collision 350 351 layout.use_property_split = True 352 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 353 354 coll = md.settings 355 356 if not coll: 357 collision_warning(layout) 358 return 359 360 settings = context.object.collision 361 362 layout.active = settings.use 363 364 col = flow.column() 365 col.prop(settings, "permeability", slider=True) 366 col.prop(settings, "stickiness") 367 col.prop(settings, "use_particle_kill") 368 369 col = flow.column() 370 sub = col.column(align=True) 371 sub.prop(settings, "damping_factor", text="Damping", slider=True) 372 sub.prop(settings, "damping_random", text="Randomize", slider=True) 373 374 col = flow.column() 375 sub = col.column(align=True) 376 sub.prop(settings, "friction_factor", text="Friction", slider=True) 377 sub.prop(settings, "friction_random", text="Randomize", slider=True) 378 379 380class PHYSICS_PT_collision_softbody(PhysicButtonsPanel, Panel): 381 bl_label = "Softbody And Cloth" 382 bl_parent_id = "PHYSICS_PT_collision" 383 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 384 385 @classmethod 386 def poll(cls, context): 387 if not PhysicButtonsPanel.poll_collision(context): 388 return False 389 390 return (context.engine in cls.COMPAT_ENGINES) 391 392 def draw(self, context): 393 layout = self.layout 394 395 layout.use_property_split = True 396 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 397 398 md = context.collision 399 coll = md.settings 400 401 if not coll: 402 collision_warning(layout) 403 return 404 405 settings = context.object.collision 406 407 layout.active = settings.use 408 409 col = flow.column() 410 col.prop(settings, "damping", text="Damping", slider=True) 411 412 col = flow.column() 413 col.prop(settings, "thickness_outer", text="Thickness Outer", slider=True) 414 415 col = flow.column() 416 col.prop(settings, "thickness_inner", text="Inner", slider=True) 417 418 col = flow.column() 419 col.prop(settings, "cloth_friction") 420 421 col = flow.column() 422 col.prop(settings, "use_culling") 423 424 col = flow.column() 425 col.prop(settings, "use_normal") 426 427 428classes = ( 429 PHYSICS_PT_field, 430 PHYSICS_PT_field_settings, 431 PHYSICS_PT_field_settings_kink, 432 PHYSICS_PT_field_settings_texture_select, 433 PHYSICS_PT_field_falloff, 434 PHYSICS_PT_field_falloff_angular, 435 PHYSICS_PT_field_falloff_radial, 436 PHYSICS_PT_collision, 437 PHYSICS_PT_collision_particle, 438 PHYSICS_PT_collision_softbody, 439) 440 441 442if __name__ == "__main__": # only for live edit. 443 from bpy.utils import register_class 444 for cls in classes: 445 register_class(cls) 446