ffc0c6830c6dac98a4a5cdfb6aeb08cbd4439f55
[blender.git] / release / scripts / startup / bl_ui / properties_particle.py
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 import bpy
21 from bpy.types import Panel, Menu
22 from rna_prop_ui import PropertyPanel
23 from bpy.app.translations import pgettext_iface as iface_
24 from bl_operators.presets import PresetMenu
25
26 from .properties_physics_common import (
27     point_cache_ui,
28     effector_weights_ui,
29     basic_force_field_settings_ui,
30     basic_force_field_falloff_ui,
31 )
32
33
34 def particle_panel_enabled(context, psys):
35     if psys is None:
36         return True
37     phystype = psys.settings.physics_type
38     if psys.settings.type in {'EMITTER', 'REACTOR'} and phystype in {'NO', 'KEYED'}:
39         return True
40     else:
41         return (psys.point_cache.is_baked is False) and (not psys.is_edited) and (not context.particle_system_editable)
42
43
44 def particle_panel_poll(cls, context):
45     psys = context.particle_system
46     engine = context.engine
47     settings = 0
48
49     if psys:
50         settings = psys.settings
51     elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
52         settings = context.space_data.pin_id
53
54     if not settings:
55         return False
56
57     return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES)
58
59
60 def particle_get_settings(context):
61     if context.particle_system:
62         return context.particle_system.settings
63     elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
64         return context.space_data.pin_id
65     return None
66
67
68 class PARTICLE_MT_specials(Menu):
69     bl_label = "Particle Specials"
70     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
71
72     def draw(self, context):
73         layout = self.layout
74
75         props = layout.operator("particle.copy_particle_systems", text="Copy Active to Selected Objects")
76         props.use_active = True
77         props.remove_target_particles = False
78
79         props = layout.operator("particle.copy_particle_systems", text="Copy All to Selected Objects")
80         props.use_active = False
81         props.remove_target_particles = True
82
83         layout.operator("particle.duplicate_particle_system")
84
85
86 class PARTICLE_PT_hair_dynamics_presets(PresetMenu):
87     bl_label = "Hair Dynamics Presets"
88     preset_subdir = "hair_dynamics"
89     preset_operator = "script.execute_preset"
90     preset_add_operator = "particle.hair_dynamics_preset_add"
91     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
92
93
94 class ParticleButtonsPanel:
95     bl_space_type = 'PROPERTIES'
96     bl_region_type = 'WINDOW'
97     bl_context = "particle"
98
99     @classmethod
100     def poll(cls, context):
101         return particle_panel_poll(cls, context)
102
103
104 def find_modifier(ob, psys):
105     for md in ob.modifiers:
106         if md.type == 'PARTICLE_SYSTEM':
107             if md.particle_system == psys:
108                 return md
109
110
111 class PARTICLE_UL_particle_systems(bpy.types.UIList):
112
113     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
114         ob = data
115         psys = item
116
117         if self.layout_type in {'DEFAULT', 'COMPACT'}:
118             md = find_modifier(ob, psys)
119
120             layout.prop(psys, "name", text="", emboss=False, icon_value=icon)
121             if md:
122                 layout.prop(md, "show_render", emboss=False, icon_only=True,
123                             icon='RESTRICT_RENDER_OFF' if md.show_render else 'RESTRICT_RENDER_ON')
124                 layout.prop(md, "show_viewport", emboss=False, icon_only=True,
125                             icon='RESTRICT_VIEW_OFF' if md.show_viewport else 'RESTRICT_VIEW_ON')
126
127         elif self.layout_type == 'GRID':
128             layout.alignment = 'CENTER'
129             layout.label(text="", icon_value=icon)
130
131
132 class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel):
133     bl_label = ""
134     bl_options = {'HIDE_HEADER'}
135     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
136
137     @classmethod
138     def poll(cls, context):
139         engine = context.engine
140         return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES)
141
142     def draw(self, context):
143         layout = self.layout
144
145         ob = context.object
146         psys = context.particle_system
147         part = 0
148
149         if ob:
150             row = layout.row()
151
152             row.template_list("PARTICLE_UL_particle_systems", "particle_systems", ob, "particle_systems",
153                               ob.particle_systems, "active_index", rows=2)
154
155             col = row.column(align=True)
156             col.operator("object.particle_system_add", icon='ZOOMIN', text="")
157             col.operator("object.particle_system_remove", icon='ZOOMOUT', text="")
158             col.menu("PARTICLE_MT_specials", icon='DOWNARROW_HLT', text="")
159
160         if psys is None:
161             part = particle_get_settings(context)
162
163             layout.operator("object.particle_system_add", icon='ZOOMIN', text="New")
164
165             if part is None:
166                 return
167
168             layout.template_ID(context.space_data, "pin_id")
169
170             if part.is_fluid:
171                 layout.label(text="Settings used for fluid")
172                 return
173
174             layout.prop(part, "type", text="Type")
175
176         elif not psys.settings:
177             split = layout.split(factor=0.32)
178
179             col = split.column()
180             col.label(text="Settings:")
181
182             col = split.column()
183             col.template_ID(psys, "settings", new="particle.new")
184         else:
185             part = psys.settings
186
187             split = layout.split(factor=0.32)
188             col = split.column()
189             if part.is_fluid is False:
190                 col.label(text="Settings:")
191                 col.label(text="Type:")
192
193             col = split.column()
194             if part.is_fluid is False:
195                 row = col.row()
196                 row.enabled = particle_panel_enabled(context, psys)
197                 row.template_ID(psys, "settings", new="particle.new")
198
199             if part.is_fluid:
200                 layout.label(text=iface_("%d fluid particles for this frame") % part.count, translate=False)
201                 return
202
203             row = col.row()
204             row.enabled = particle_panel_enabled(context, psys)
205             row.prop(part, "type", text="")
206             row.prop(psys, "seed")
207
208         if part:
209             split = layout.split(factor=0.65)
210             if part.type == 'HAIR':
211                 if psys is not None and psys.is_edited:
212                     split.operator("particle.edited_clear", text="Free Edit")
213                 else:
214                     row = split.row()
215                     row.enabled = particle_panel_enabled(context, psys)
216                     row.prop(part, "regrow_hair")
217                     row.prop(part, "use_advanced_hair")
218                 row = split.row()
219                 row.enabled = particle_panel_enabled(context, psys)
220                 row.prop(part, "hair_step")
221                 if psys is not None and psys.is_edited:
222                     if psys.is_global_hair:
223                         row = layout.row(align=True)
224                         row.operator("particle.connect_hair").all = False
225                         row.operator("particle.connect_hair", text="Connect All").all = True
226                     else:
227                         row = layout.row(align=True)
228                         row.operator("particle.disconnect_hair").all = False
229                         row.operator("particle.disconnect_hair", text="Disconnect All").all = True
230             elif psys is not None and part.type == 'REACTOR':
231                 split.enabled = particle_panel_enabled(context, psys)
232                 split.prop(psys, "reactor_target_object")
233                 split.prop(psys, "reactor_target_particle_system", text="Particle System")
234
235
236 class PARTICLE_PT_emission(ParticleButtonsPanel, Panel):
237     bl_label = "Emission"
238     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
239
240     @classmethod
241     def poll(cls, context):
242         psys = context.particle_system
243         settings = particle_get_settings(context)
244
245         if settings is None:
246             return False
247         if settings.is_fluid:
248             return False
249         if particle_panel_poll(PARTICLE_PT_emission, context):
250             return psys is None or not context.particle_system.point_cache.use_external
251         return False
252
253     def draw(self, context):
254         layout = self.layout
255
256         psys = context.particle_system
257         part = particle_get_settings(context)
258
259         layout.use_property_split = True
260
261         layout.enabled = particle_panel_enabled(context, psys) and (psys is None or not psys.has_multiple_caches)
262
263         col = layout.column()
264         col.active = part.emit_from == 'VERT' or part.distribution != 'GRID'
265         col.prop(part, "count")
266
267         if part.type == 'HAIR':
268             col.prop(part, "hair_length")
269             if not part.use_advanced_hair:
270                 row = layout.row()
271                 col.prop(part, "use_modifier_stack")
272                 return
273
274         if part.type != 'HAIR':
275
276             col = layout.column()
277
278             sub = col.column(align=True)
279             sub.prop(part, "frame_start", text="Frame Start")
280             sub.prop(part, "frame_end", text="End")
281
282             col.prop(part, "lifetime")
283             col.prop(part, "lifetime_random", slider=True, text="Lifetime Randomness")
284
285
286 class PARTICLE_PT_emission_source(ParticleButtonsPanel, Panel):
287     bl_label = "Source"
288     bl_parent_id = "PARTICLE_PT_emission"
289     bl_options = {'DEFAULT_CLOSED'}
290     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
291
292     def draw(self, context):
293         layout = self.layout
294
295         part = particle_get_settings(context)
296
297         layout.use_property_split = True
298
299         col = layout.column()
300         col.prop(part, "emit_from")
301         col.prop(part, "use_modifier_stack")
302         if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
303             col.prop(part, "distribution")
304
305         if part.emit_from == 'VERT':
306             col.prop(part, "use_emit_random", text="Random Order")
307         elif part.distribution == 'GRID':
308             col.label(text="Grid")
309             col.prop(part, "invert_grid")
310             col.prop(part, "hexagonal_grid")
311         else:
312             col.prop(part, "use_emit_random")
313             col.prop(part, "use_even_distribution")
314
315         if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
316
317             if part.distribution == 'JIT':
318                 col.prop(part, "userjit", text="Particles/Face")
319                 col.prop(part, "jitter_factor", text="Jittering Amount", slider=True)
320             elif part.distribution == 'GRID':
321                 col.prop(part, "grid_resolution")
322                 col.prop(part, "grid_random", text="Random", slider=True)
323
324
325 class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
326     bl_label = "Hair Dynamics"
327     bl_options = {'DEFAULT_CLOSED'}
328     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
329
330     @classmethod
331     def poll(cls, context):
332         psys = context.particle_system
333         engine = context.engine
334         if psys is None:
335             return False
336         if psys.settings is None:
337             return False
338         return psys.settings.type == 'HAIR' and (engine in cls.COMPAT_ENGINES)
339
340     def draw_header(self, context):
341         psys = context.particle_system
342         self.layout.prop(psys, "use_hair_dynamics", text="")
343
344     def draw_header_preset(self, context):
345         psys = context.particle_system
346
347         if not psys.cloth:
348             return
349
350         layout = self.layout
351         layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
352         PARTICLE_PT_hair_dynamics_presets.draw_panel_header(layout)
353
354     def draw(self, context):
355         layout = self.layout
356
357         psys = context.particle_system
358
359         if not psys.cloth:
360             layout.label(text="Hair dynamics disabled")
361             return
362
363         cloth_md = psys.cloth
364         cloth = cloth_md.settings
365         result = cloth_md.solver_result
366
367         layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
368
369         layout.use_property_split = True
370
371         layout.separator()
372
373         col = layout.column()
374         col.prop(cloth, "quality", text="Quality Steps", slider=True)
375         col.prop(psys.settings, "show_hair_grid", text="Display Hair Grid")
376
377         layout.separator()
378
379         col = layout.column()
380         col.prop(cloth, "pin_stiffness", text="Pin Goal Strength")
381
382         layout.separator()
383
384         if result:
385             box = layout.box()
386
387             if not result.status:
388                 label = " "
389                 icon = 'NONE'
390             elif result.status == {'SUCCESS'}:
391                 label = "Success"
392                 icon = 'NONE'
393             elif result.status - {'SUCCESS'} == {'NO_CONVERGENCE'}:
394                 label = "No Convergence"
395                 icon = 'ERROR'
396             else:
397                 label = "ERROR"
398                 icon = 'ERROR'
399             box.label(text=label, icon=icon)
400             box.label(text="Iterations: %d .. %d (avg. %d)" %
401                       (result.min_iterations, result.max_iterations, result.avg_iterations))
402             box.label(text="Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
403
404
405 class PARTICLE_PT_hair_dynamics_structure(ParticleButtonsPanel, Panel):
406     bl_label = "Structure"
407     bl_parent_id = "PARTICLE_PT_hair_dynamics"
408     bl_options = {'DEFAULT_CLOSED'}
409     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
410
411     @classmethod
412     def poll(cls, context):
413         return context.particle_system.cloth is not None
414
415     def draw(self, context):
416         layout = self.layout
417
418         psys = context.particle_system
419         cloth_md = psys.cloth
420         cloth = cloth_md.settings
421         result = cloth_md.solver_result
422
423         layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
424
425         layout.use_property_split = True
426
427         col = layout.column()
428         col.prop(cloth, "mass")
429         sub = col.column(align=True)
430         sub.prop(cloth, "bending_stiffness", text="Stiffness")
431         sub.prop(psys.settings, "bending_random", text="Random")
432         col.prop(cloth, "bending_damping", text="Damping")
433         # XXX has no noticeable effect with stiff hair structure springs
434         #col.prop(cloth, "spring_damping", text="Damping")
435
436
437 class PARTICLE_PT_hair_dynamics_volume(ParticleButtonsPanel, Panel):
438     bl_label = "Volume"
439     bl_parent_id = "PARTICLE_PT_hair_dynamics"
440     bl_options = {'DEFAULT_CLOSED'}
441     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
442
443     @classmethod
444     def poll(cls, context):
445         return context.particle_system.cloth is not None
446
447     def draw(self, context):
448         layout = self.layout
449
450         psys = context.particle_system
451         cloth_md = psys.cloth
452         cloth = cloth_md.settings
453         result = cloth_md.solver_result
454
455         layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
456
457         layout.use_property_split = True
458
459         col = layout.column()
460         col.prop(cloth, "air_damping", text="Air Drag")
461         col.prop(cloth, "internal_friction", slider=True)
462         col.prop(cloth, "voxel_cell_size")
463
464         col.separator()
465
466         col.prop(cloth, "density_target", text="Density Target")
467         col.prop(cloth, "density_strength", slider=True, text="Density Strength")
468
469
470 class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
471     bl_label = "Cache"
472     bl_options = {'DEFAULT_CLOSED'}
473     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
474
475     @classmethod
476     def poll(cls, context):
477         psys = context.particle_system
478         engine = context.engine
479         if psys is None:
480             return False
481         if psys.settings is None:
482             return False
483         if psys.settings.is_fluid:
484             return False
485         phystype = psys.settings.physics_type
486         if phystype == 'NO' or phystype == 'KEYED':
487             return False
488         return (
489             (psys.settings.type in {'EMITTER', 'REACTOR'} or
490              (psys.settings.type == 'HAIR' and
491               (psys.use_hair_dynamics or psys.point_cache.is_baked))) and
492             engine in cls.COMPAT_ENGINES
493         )
494
495     def draw(self, context):
496         psys = context.particle_system
497
498         point_cache_ui(self, context, psys.point_cache, True, 'HAIR' if (psys.settings.type == 'HAIR') else 'PSYS')
499
500
501 class PARTICLE_PT_velocity(ParticleButtonsPanel, Panel):
502     bl_label = "Velocity"
503     bl_options = {'DEFAULT_CLOSED'}
504     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
505
506     @classmethod
507     def poll(cls, context):
508         if particle_panel_poll(PARTICLE_PT_velocity, context):
509             psys = context.particle_system
510             settings = particle_get_settings(context)
511
512             if settings.type == 'HAIR' and not settings.use_advanced_hair:
513                 return False
514             return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
515         else:
516             return False
517
518     def draw(self, context):
519         layout = self.layout
520
521         psys = context.particle_system
522         part = particle_get_settings(context)
523
524         layout.enabled = particle_panel_enabled(context, psys)
525         layout.use_property_split = True
526
527         col = layout.column()
528         col.prop(part, "normal_factor")
529         sub = col.column(align=True)
530         sub.prop(part, "tangent_factor", text="Tangent")
531         sub.prop(part, "tangent_phase", slider=True, text="Tangent Phase")
532
533         col.separator()
534
535         col.prop(part, "object_align_factor")
536
537         col.separator()
538
539         if part.emit_from == 'PARTICLE':
540             col.prop(part, "particle_factor")
541         else:
542             col.prop(part, "object_factor", slider=True)
543         col.prop(part, "factor_random", text="Randomize")
544
545         # if part.type=='REACTOR':
546         #     sub.prop(part, "reactor_factor")
547         #     sub.prop(part, "reaction_shape", slider=True)
548
549
550 class PARTICLE_PT_rotation(ParticleButtonsPanel, Panel):
551     bl_label = "Rotation"
552     bl_options = {'DEFAULT_CLOSED'}
553     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
554
555     @classmethod
556     def poll(cls, context):
557         if particle_panel_poll(PARTICLE_PT_rotation, context):
558             psys = context.particle_system
559             settings = particle_get_settings(context)
560
561             if settings.type == 'HAIR' and not settings.use_advanced_hair:
562                 return False
563             return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
564         else:
565             return False
566
567     def draw_header(self, context):
568         psys = context.particle_system
569         if psys:
570             part = psys.settings
571         else:
572             part = context.space_data.pin_id
573
574         self.layout.prop(part, "use_rotations", text="")
575
576     def draw(self, context):
577         layout = self.layout
578
579         psys = context.particle_system
580         if psys:
581             part = psys.settings
582         else:
583             part = context.space_data.pin_id
584
585         layout.enabled = particle_panel_enabled(context, psys) and part.use_rotations
586         layout.use_property_split = True
587
588         col = layout.column()
589
590         col.prop(part, "rotation_mode")
591         col.prop(part, "rotation_factor_random", slider=True, text="Randomize")
592
593         col.separator()
594
595         col.prop(part, "phase_factor", slider=True)
596         col.prop(part, "phase_factor_random", text="Randomize Phase ", slider=True)
597
598         if part.type != 'HAIR':
599             col.prop(part, "use_dynamic_rotation")
600
601
602 class PARTICLE_PT_rotation_angular_velocity(ParticleButtonsPanel, Panel):
603     bl_label = "Angular Velocity"
604     bl_parent_id = "PARTICLE_PT_rotation"
605     bl_options = {'DEFAULT_CLOSED'}
606     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
607
608     def draw(self, context):
609         layout = self.layout
610
611         psys = context.particle_system
612         if psys:
613             part = psys.settings
614         else:
615             part = context.space_data.pin_id
616
617         layout.enabled = particle_panel_enabled(context, psys) and part.use_rotations
618         layout.use_property_split = True
619
620         col = layout.column()
621
622         col.prop(part, "angular_velocity_mode", text="Axis")
623         sub = col.column(align=True)
624         sub.active = part.angular_velocity_mode != 'NONE'
625         sub.prop(part, "angular_velocity_factor", text="Amount")
626
627
628 class PARTICLE_PT_physics(ParticleButtonsPanel, Panel):
629     bl_label = "Physics"
630     bl_options = {'DEFAULT_CLOSED'}
631     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
632
633     @classmethod
634     def poll(cls, context):
635         if particle_panel_poll(PARTICLE_PT_physics, context):
636             psys = context.particle_system
637             settings = particle_get_settings(context)
638
639             if settings.type == 'HAIR' and not settings.use_advanced_hair:
640                 return False
641             return psys is None or not psys.point_cache.use_external
642         else:
643             return False
644
645     def draw(self, context):
646         layout = self.layout
647         layout.use_property_split = True
648
649         psys = context.particle_system
650         part = particle_get_settings(context)
651
652         layout.enabled = particle_panel_enabled(context, psys)
653
654         layout.prop(part, "physics_type")
655
656         col = layout.column()
657         if part.physics_type != 'NO':
658             col = col.column()
659             col.prop(part, "mass")
660             col.prop(part, "use_multiply_size_mass", text="Multiply mass with size")
661
662         if part.physics_type == 'FLUID':
663             fluid = part.fluid
664
665             col.separator()
666             col.prop(fluid, "solver")
667             col.prop(fluid, "stiffness", text="Stiffness")
668             col.prop(fluid, "linear_viscosity", text="Viscosity")
669             col.prop(fluid, "buoyancy", text="Buoyancy", slider=True)
670
671         elif part.physics_type == 'KEYED':
672
673             sub = col.column()
674             sub.active = not psys.use_keyed_timing
675             sub.prop(part, "keyed_loops", text="Loops")
676             if psys:
677                 col.prop(psys, "use_keyed_timing", text="Use Timing")
678
679             col.label(text="Keys")
680
681
682 class PARTICLE_PT_physics_fluid_advanced(ParticleButtonsPanel, Panel):
683     bl_label = "Advanced"
684     bl_parent_id = "PARTICLE_PT_physics"
685     bl_options = {'DEFAULT_CLOSED'}
686     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
687
688     @classmethod
689     def poll(cls, context):
690         part = particle_get_settings(context)
691         fluid = part.fluid
692         if part.physics_type == 'FLUID':
693             return True
694         else:
695             return False
696
697     def draw(self, context):
698         layout = self.layout
699         layout.use_property_split = True
700
701         psys = context.particle_system
702         part = particle_get_settings(context)
703         fluid = part.fluid
704
705         col = layout.column()
706
707         if fluid.solver == 'DDR':
708             sub = col.column()
709             sub.prop(fluid, "repulsion", slider=fluid.factor_repulsion)
710             sub.prop(fluid, "factor_repulsion")
711
712             sub.prop(fluid, "stiff_viscosity", slider=fluid.factor_stiff_viscosity)
713             sub.prop(fluid, "factor_stiff_viscosity")
714
715         sub = col.column()
716         sub.prop(fluid, "fluid_radius", slider=fluid.factor_radius)
717         sub.prop(fluid, "factor_radius")
718
719         sub.prop(fluid, "rest_density", slider=fluid.use_factor_density)
720         sub.prop(fluid, "use_factor_density")
721
722         if fluid.solver == 'CLASSICAL':
723             # With the classical solver, it is possible to calculate the
724             # spacing between particles when the fluid is at rest. This
725             # makes it easier to set stable initial conditions.
726             particle_volume = part.mass / fluid.rest_density
727             spacing = pow(particle_volume, 1.0 / 3.0)
728
729             sub.label(text="Spacing: %g" % spacing)
730
731
732 class PARTICLE_PT_physics_fluid_springs(ParticleButtonsPanel, Panel):
733     bl_label = "Springs"
734     bl_parent_id = "PARTICLE_PT_physics"
735     bl_options = {'DEFAULT_CLOSED'}
736     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
737
738     @classmethod
739     def poll(cls, context):
740         part = particle_get_settings(context)
741         fluid = part.fluid
742         if part.physics_type == 'FLUID' and fluid.solver == 'DDR':
743             return True
744         else:
745             return False
746
747     def draw(self, context):
748         layout = self.layout
749         layout.use_property_split = True
750
751         psys = context.particle_system
752         part = particle_get_settings(context)
753         fluid = part.fluid
754
755         col = layout.column()
756
757         col.prop(fluid, "spring_force", text="Force")
758
759
760 class PARTICLE_PT_physics_fluid_springs_viscoelastic(ParticleButtonsPanel, Panel):
761     bl_label = "Viscoelastic Springs"
762     bl_parent_id = "PARTICLE_PT_physics_fluid_springs"
763     bl_options = {'DEFAULT_CLOSED'}
764     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
765
766     @classmethod
767     def poll(cls, context):
768         part = particle_get_settings(context)
769         fluid = part.fluid
770         if part.physics_type == 'FLUID' and fluid.solver == 'DDR':
771             return True
772         else:
773             return False
774
775     def draw_header(self, context):
776         psys = context.particle_system
777         part = particle_get_settings(context)
778         fluid = part.fluid
779
780         self.layout.prop(fluid, "use_viscoelastic_springs", text="")
781
782     def draw(self, context):
783         layout = self.layout
784         layout.use_property_split = True
785
786         psys = context.particle_system
787         part = particle_get_settings(context)
788         fluid = part.fluid
789
790         col = layout.column()
791         col.active = fluid.use_viscoelastic_springs
792         col.prop(fluid, "yield_ratio", slider=True)
793         col.prop(fluid, "plasticity", slider=True)
794
795         col.separator()
796
797         col.prop(fluid, "use_initial_rest_length")
798         col.prop(fluid, "spring_frames", text="Frames")
799
800
801 class PARTICLE_PT_physics_fluid_springs_advanced(ParticleButtonsPanel, Panel):
802     bl_label = "Advanced"
803     bl_parent_id = "PARTICLE_PT_physics_fluid_springs"
804     bl_options = {'DEFAULT_CLOSED'}
805     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
806
807     @classmethod
808     def poll(cls, context):
809         part = particle_get_settings(context)
810         fluid = part.fluid
811         if part.physics_type == 'FLUID' and fluid.solver == 'DDR':
812             return True
813         else:
814             return False
815
816     def draw(self, context):
817         layout = self.layout
818         layout.use_property_split = True
819
820         psys = context.particle_system
821         part = particle_get_settings(context)
822         fluid = part.fluid
823
824         sub = layout.column()
825         sub.prop(fluid, "rest_length", slider=fluid.factor_rest_length)
826         sub.prop(fluid, "factor_rest_length")
827
828
829 class PARTICLE_PT_physics_boids_movement(ParticleButtonsPanel, Panel):
830     bl_label = "Movement"
831     bl_parent_id = "PARTICLE_PT_physics"
832     bl_options = {'DEFAULT_CLOSED'}
833     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
834
835     @classmethod
836     def poll(cls, context):
837         part = particle_get_settings(context)
838         return part.physics_type in {'BOIDS'}
839
840     def draw(self, context):
841         layout = self.layout
842         layout.use_property_split = True
843
844         psys = context.particle_system
845         part = particle_get_settings(context)
846         boids = part.boids
847
848         col = layout.column()
849
850         col.prop(boids, "use_flight")
851         col.prop(boids, "use_land")
852         col.prop(boids, "use_climb")
853
854         col = layout.column()
855
856         col.active = boids.use_flight
857         sub = col.column()
858         sub.prop(boids, "air_speed_max")
859         sub.prop(boids, "air_speed_min", slider=True)
860         col.prop(boids, "air_acc_max", slider=True)
861         col.prop(boids, "air_ave_max", slider=True)
862         col.prop(boids, "air_personal_space")
863         row = col.row(align=True)
864         row.active = (boids.use_land or boids.use_climb) and boids.use_flight
865         row.prop(boids, "land_smooth")
866
867         layout.separator()
868
869         col = layout.column()
870         col.active = boids.use_land or boids.use_climb
871         col.prop(boids, "land_speed_max")
872         col.prop(boids, "land_jump_speed")
873         col.prop(boids, "land_acc_max", slider=True)
874         col.prop(boids, "land_ave_max", slider=True)
875         col.prop(boids, "land_personal_space")
876         col.prop(boids, "land_stick_force")
877
878         layout.separator()
879
880         layout.prop(part, "collision_group")
881
882
883 class PARTICLE_PT_physics_boids_battle(ParticleButtonsPanel, Panel):
884     bl_label = "Battle"
885     bl_parent_id = "PARTICLE_PT_physics"
886     bl_options = {'DEFAULT_CLOSED'}
887     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
888
889     @classmethod
890     def poll(cls, context):
891         part = particle_get_settings(context)
892         return part.physics_type in {'BOIDS'}
893
894     def draw(self, context):
895         layout = self.layout
896         layout.use_property_split = True
897
898         psys = context.particle_system
899         part = particle_get_settings(context)
900         boids = part.boids
901
902         col = layout.column()
903
904         col.prop(boids, "health")
905         col.prop(boids, "strength")
906         col.prop(boids, "aggression")
907         col.prop(boids, "accuracy")
908         col.prop(boids, "range")
909
910
911 class PARTICLE_PT_physics_boids_misc(ParticleButtonsPanel, Panel):
912     bl_label = "Misc"
913     bl_parent_id = "PARTICLE_PT_physics"
914     bl_options = {'DEFAULT_CLOSED'}
915     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
916
917     @classmethod
918     def poll(cls, context):
919         part = particle_get_settings(context)
920         return part.physics_type in {'BOIDS'}
921
922     def draw(self, context):
923         layout = self.layout
924         layout.use_property_split = True
925
926         psys = context.particle_system
927         part = particle_get_settings(context)
928         boids = part.boids
929
930         col = layout.column()
931
932         col.prop(boids, "bank", slider=True)
933         col.prop(boids, "pitch", slider=True)
934         col.prop(boids, "height", slider=True)
935
936
937 class PARTICLE_PT_physics_relations(ParticleButtonsPanel, Panel):
938     bl_label = "Relations"
939     bl_parent_id = "PARTICLE_PT_physics"
940     bl_options = {'DEFAULT_CLOSED'}
941     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
942
943     @classmethod
944     def poll(cls, context):
945         part = particle_get_settings(context)
946         return part.physics_type in {'KEYED', 'BOIDS', 'FLUID'}
947
948     def draw(self, context):
949         layout = self.layout
950         layout.use_property_split = True
951
952         psys = context.particle_system
953         part = particle_get_settings(context)
954
955         row = layout.row()
956         row.template_list("UI_UL_list", "particle_targets", psys, "targets",
957                           psys, "active_particle_target_index", rows=4)
958
959         col = row.column()
960         sub = col.row()
961         subsub = sub.column(align=True)
962         subsub.operator("particle.new_target", icon='ZOOMIN', text="")
963         subsub.operator("particle.target_remove", icon='ZOOMOUT', text="")
964         sub = col.row()
965         subsub = sub.column(align=True)
966         subsub.operator("particle.target_move_up", icon='TRIA_UP', text="")
967         subsub.operator("particle.target_move_down", icon='TRIA_DOWN', text="")
968
969         key = psys.active_particle_target
970
971         if key:
972             if part.physics_type == 'KEYED':
973                 col = layout.column()
974                 # doesn't work yet
975                 #col.alert = key.valid
976                 col.prop(key, "object")
977                 col.prop(key, "system", text="System")
978
979                 col.active = psys.use_keyed_timing
980                 col.prop(key, "time")
981                 col.prop(key, "duration")
982             elif part.physics_type == 'BOIDS':
983                 sub = layout.column()
984                 # doesn't work yet
985                 #sub.alert = key.valid
986                 sub.prop(key, "object")
987                 sub.prop(key, "system", text="System")
988                 layout.prop(key, "alliance")
989             elif part.physics_type == 'FLUID':
990                 sub = layout.column()
991                 # doesn't work yet
992                 #sub.alert = key.valid
993                 sub.prop(key, "object")
994                 sub.prop(key, "system", text="System")
995
996
997 class PARTICLE_PT_physics_deflection(ParticleButtonsPanel, Panel):
998     bl_label = "Deflection"
999     bl_parent_id = "PARTICLE_PT_physics"
1000     bl_options = {'DEFAULT_CLOSED'}
1001     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1002
1003     @classmethod
1004     def poll(cls, context):
1005         part = particle_get_settings(context)
1006         return part.physics_type in {'NEWTON', 'FLUID'}
1007
1008     def draw(self, context):
1009         layout = self.layout
1010         layout.use_property_split = True
1011
1012         psys = context.particle_system
1013         part = particle_get_settings(context)
1014
1015         layout.enabled = particle_panel_enabled(context, psys)
1016
1017         col = layout.column()
1018         col.prop(part, "use_size_deflect")
1019         col.prop(part, "use_die_on_collision")
1020
1021         col.prop(part, "collision_group")
1022
1023
1024 class PARTICLE_PT_physics_forces(ParticleButtonsPanel, Panel):
1025     bl_label = "Forces"
1026     bl_parent_id = "PARTICLE_PT_physics"
1027     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1028
1029     @classmethod
1030     def poll(cls, context):
1031         part = particle_get_settings(context)
1032         return part.physics_type == 'NEWTON'
1033
1034     def draw(self, context):
1035         layout = self.layout
1036         layout.use_property_split = True
1037
1038         psys = context.particle_system
1039         part = particle_get_settings(context)
1040
1041         layout.enabled = particle_panel_enabled(context, psys)
1042
1043         col = layout.column()
1044
1045         col.prop(part, "brownian_factor")
1046         col.prop(part, "drag_factor", slider=True)
1047         col.prop(part, "damping", slider=True)
1048
1049
1050 class PARTICLE_PT_physics_integration(ParticleButtonsPanel, Panel):
1051     bl_label = "Integration"
1052     bl_options = {'DEFAULT_CLOSED'}
1053     bl_parent_id = "PARTICLE_PT_physics"
1054     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1055
1056     @classmethod
1057     def poll(cls, context):
1058         part = particle_get_settings(context)
1059         return part.physics_type == 'NEWTON'
1060
1061     def draw(self, context):
1062         layout = self.layout
1063         layout.use_property_split = True
1064
1065         psys = context.particle_system
1066         part = particle_get_settings(context)
1067
1068         layout.enabled = particle_panel_enabled(context, psys)
1069
1070         col = layout.column()
1071
1072         col.prop(part, "integrator")
1073         col.prop(part, "timestep")
1074         sub = col.row()
1075         sub.prop(part, "subframes")
1076         supports_courant = part.physics_type == 'FLUID'
1077         subsub = sub.row()
1078         subsub.enabled = supports_courant
1079         subsub.prop(part, "use_adaptive_subframes", text="")
1080         if supports_courant and part.use_adaptive_subframes:
1081             col.prop(part, "courant_target", text="Threshold")
1082
1083
1084 class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel):
1085     bl_label = "Boid Brain"
1086     bl_options = {'DEFAULT_CLOSED'}
1087     bl_parent_id = "PARTICLE_PT_physics"
1088
1089     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1090
1091     @classmethod
1092     def poll(cls, context):
1093         psys = context.particle_system
1094         settings = particle_get_settings(context)
1095         engine = context.engine
1096
1097         if settings is None:
1098             return False
1099         if psys is not None and psys.point_cache.use_external:
1100             return False
1101         return settings.physics_type == 'BOIDS' and engine in cls.COMPAT_ENGINES
1102
1103     def draw(self, context):
1104         layout = self.layout
1105
1106         boids = particle_get_settings(context).boids
1107
1108         layout.enabled = particle_panel_enabled(context, context.particle_system)
1109
1110         # Currently boids can only use the first state so these are commented out for now.
1111         #row = layout.row()
1112         # row.template_list("UI_UL_list", "particle_boids", boids, "states",
1113         #                  boids, "active_boid_state_index", compact="True")
1114         #col = row.row()
1115         #sub = col.row(align=True)
1116         #sub.operator("boid.state_add", icon='ZOOMIN', text="")
1117         #sub.operator("boid.state_del", icon='ZOOMOUT', text="")
1118         #sub = row.row(align=True)
1119         #sub.operator("boid.state_move_up", icon='TRIA_UP', text="")
1120         #sub.operator("boid.state_move_down", icon='TRIA_DOWN', text="")
1121
1122         state = boids.active_boid_state
1123
1124         #layout.prop(state, "name", text="State name")
1125
1126         row = layout.row()
1127         row.prop(state, "ruleset_type")
1128         if state.ruleset_type == 'FUZZY':
1129             row.prop(state, "rule_fuzzy", slider=True)
1130         else:
1131             row.label(text="")
1132
1133         row = layout.row()
1134         row.template_list("UI_UL_list", "particle_boids_rules", state,
1135                           "rules", state, "active_boid_rule_index", rows=4)
1136
1137         col = row.column()
1138         sub = col.row()
1139         subsub = sub.column(align=True)
1140         subsub.operator_menu_enum("boid.rule_add", "type", icon='ZOOMIN', text="")
1141         subsub.operator("boid.rule_del", icon='ZOOMOUT', text="")
1142         sub = col.row()
1143         subsub = sub.column(align=True)
1144         subsub.operator("boid.rule_move_up", icon='TRIA_UP', text="")
1145         subsub.operator("boid.rule_move_down", icon='TRIA_DOWN', text="")
1146
1147         rule = state.active_boid_rule
1148
1149         if rule:
1150             row = layout.row()
1151             row.prop(rule, "name", text="")
1152             # somebody make nice icons for boids here please! -jahka
1153             row.prop(rule, "use_in_air", icon='TRIA_UP', text="")
1154             row.prop(rule, "use_on_land", icon='TRIA_DOWN', text="")
1155
1156             row = layout.row()
1157
1158             if rule.type == 'GOAL':
1159                 row.prop(rule, "object")
1160                 row = layout.row()
1161                 row.prop(rule, "use_predict")
1162             elif rule.type == 'AVOID':
1163                 row.prop(rule, "object")
1164                 row = layout.row()
1165                 row.prop(rule, "use_predict")
1166                 row.prop(rule, "fear_factor")
1167             elif rule.type == 'FOLLOW_PATH':
1168                 row.label(text="Not yet functional")
1169             elif rule.type == 'AVOID_COLLISION':
1170                 row.prop(rule, "use_avoid")
1171                 row.prop(rule, "use_avoid_collision")
1172                 row.prop(rule, "look_ahead")
1173             elif rule.type == 'FOLLOW_LEADER':
1174                 row.prop(rule, "object", text="")
1175                 row.prop(rule, "distance")
1176                 row = layout.row()
1177                 row.prop(rule, "use_line")
1178                 sub = row.row()
1179                 sub.active = rule.line
1180                 sub.prop(rule, "queue_count")
1181             elif rule.type == 'AVERAGE_SPEED':
1182                 row.prop(rule, "speed", slider=True)
1183                 row.prop(rule, "wander", slider=True)
1184                 row.prop(rule, "level", slider=True)
1185             elif rule.type == 'FIGHT':
1186                 row.prop(rule, "distance")
1187                 row.prop(rule, "flee_distance")
1188
1189
1190 class PARTICLE_PT_render(ParticleButtonsPanel, Panel):
1191     bl_label = "Render"
1192     bl_options = {'DEFAULT_CLOSED'}
1193     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1194
1195     @classmethod
1196     def poll(cls, context):
1197         settings = particle_get_settings(context)
1198         engine = context.engine
1199         if settings is None:
1200             return False
1201
1202         return engine in cls.COMPAT_ENGINES
1203
1204     def draw(self, context):
1205         layout = self.layout
1206         layout.use_property_split = True
1207
1208         psys = context.particle_system
1209         part = particle_get_settings(context)
1210
1211         layout.prop(part, "render_type", text="Render As")
1212
1213         if part.type == 'EMITTER' or \
1214            (part.render_type in {'OBJECT', 'COLLECTION'} and part.type == 'HAIR'):
1215             if part.render_type not in {'NONE'}:
1216
1217                 col = layout.column(align=True)
1218                 col.prop(part, "particle_size", text="Scale")
1219                 col.prop(part, "size_random", slider=True, text="Scale Randomness")
1220
1221         if psys:
1222             col = layout.column()
1223             if part.render_type not in {'OBJECT', 'COLLECTION', 'NONE'}:
1224                 # col.enabled = False
1225                 col.prop(part, "material_slot", text="Material")
1226                 col.prop(psys, "parent", text="Coordinate System")
1227
1228
1229 class PARTICLE_PT_render_extra(ParticleButtonsPanel, Panel):
1230     bl_label = "Extra"
1231     bl_parent_id = "PARTICLE_PT_render"
1232     bl_options = {'DEFAULT_CLOSED'}
1233     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1234
1235     @classmethod
1236     def poll(cls, context):
1237         part = particle_get_settings(context)
1238         return part.render_type != 'NONE'
1239
1240     def draw(self, context):
1241         layout = self.layout
1242         layout.use_property_split = True
1243
1244         psys = context.particle_system
1245         ob = context.object
1246         part = particle_get_settings(context)
1247
1248         col = layout.column()
1249
1250         col = layout.column()
1251         col.prop(part, "use_parent_particles", text="Parent Particles")
1252         col.prop(part, "show_unborn", text="Unborn")
1253         col.prop(part, "use_dead", text="Dead")
1254
1255
1256 class PARTICLE_PT_render_line(ParticleButtonsPanel, Panel):
1257     bl_label = "Line"
1258     bl_parent_id = "PARTICLE_PT_render"
1259     bl_options = {'DEFAULT_CLOSED'}
1260     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1261
1262     @classmethod
1263     def poll(cls, context):
1264         part = particle_get_settings(context)
1265         return part.render_type == 'LINE'
1266
1267     def draw(self, context):
1268         layout = self.layout
1269         layout.use_property_split = True
1270
1271         psys = context.particle_system
1272         ob = context.object
1273         part = particle_get_settings(context)
1274
1275         col = layout.column()
1276
1277         col.separator()
1278         sub = col.column(align=True)
1279         sub.prop(part, "line_length_tail", text="Length Tail")
1280         sub.prop(part, "line_length_head", text="Head")
1281         col.prop(part, "use_velocity_length", text="Velocity Length")
1282
1283
1284 class PARTICLE_PT_render_path(ParticleButtonsPanel, Panel):
1285     bl_label = "Path"
1286     bl_parent_id = "PARTICLE_PT_render"
1287     bl_options = {'DEFAULT_CLOSED'}
1288     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1289
1290     @classmethod
1291     def poll(cls, context):
1292         part = particle_get_settings(context)
1293         return part.render_type == 'PATH'
1294
1295     def draw(self, context):
1296         layout = self.layout
1297         layout.use_property_split = True
1298
1299         psys = context.particle_system
1300         ob = context.object
1301         part = particle_get_settings(context)
1302
1303         col = layout.column()
1304
1305         col.prop(part, "use_strand_primitive")
1306         sub = col.column()
1307         sub.active = (part.use_strand_primitive is False)
1308         sub.prop(part, "use_render_adaptive")
1309         sub = col.column()
1310         sub.active = part.use_render_adaptive or part.use_strand_primitive is True
1311         sub.prop(part, "adaptive_angle")
1312         sub = col.column()
1313         sub.active = (part.use_render_adaptive is True and part.use_strand_primitive is False)
1314         sub.prop(part, "adaptive_pixel")
1315         col.prop(part, "use_hair_bspline")
1316         col.prop(part, "render_step", text="Steps")
1317
1318
1319 class PARTICLE_PT_render_path_timing(ParticleButtonsPanel, Panel):
1320     bl_label = "Timing"
1321     bl_parent_id = "PARTICLE_PT_render"
1322     bl_options = {'DEFAULT_CLOSED'}
1323     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1324
1325     @classmethod
1326     def poll(cls, context):
1327         part = particle_get_settings(context)
1328         return part.render_type == 'PATH'
1329
1330     def draw(self, context):
1331         layout = self.layout
1332         layout.use_property_split = True
1333
1334         psys = context.particle_system
1335         ob = context.object
1336         part = particle_get_settings(context)
1337
1338         col = layout.column()
1339
1340         col.prop(part, "use_absolute_path_time")
1341
1342         if part.type == 'HAIR' or psys.point_cache.is_baked:
1343             col.prop(part, "path_start", text="Start", slider=not part.use_absolute_path_time)
1344         else:
1345             col.prop(part, "trail_count")
1346
1347         col.prop(part, "path_end", text="End", slider=not part.use_absolute_path_time)
1348         col.prop(part, "length_random", text="Random", slider=True)
1349
1350
1351 class PARTICLE_PT_render_object(ParticleButtonsPanel, Panel):
1352     bl_label = "Object"
1353     bl_parent_id = "PARTICLE_PT_render"
1354     bl_options = {'DEFAULT_CLOSED'}
1355     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1356
1357     @classmethod
1358     def poll(cls, context):
1359         part = particle_get_settings(context)
1360         return part.render_type == 'OBJECT'
1361
1362     def draw(self, context):
1363         layout = self.layout
1364         layout.use_property_split = True
1365
1366         psys = context.particle_system
1367         ob = context.object
1368         part = particle_get_settings(context)
1369
1370         col = layout.column()
1371
1372         col.prop(part, "dupli_object", text="Instance Object")
1373         sub = col.column()
1374         sub.prop(part, "use_global_dupli", text="Global Coordinates")
1375         sub.prop(part, "use_rotation_dupli", text="Object Rotation")
1376         sub.prop(part, "use_scale_dupli", text="Object Scale")
1377
1378
1379 class PARTICLE_PT_render_collection(ParticleButtonsPanel, Panel):
1380     bl_label = "Collection"
1381     bl_parent_id = "PARTICLE_PT_render"
1382     bl_options = {'DEFAULT_CLOSED'}
1383     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1384
1385     @classmethod
1386     def poll(cls, context):
1387         part = particle_get_settings(context)
1388         return part.render_type == 'COLLECTION'
1389
1390     def draw(self, context):
1391         layout = self.layout
1392         layout.use_property_split = True
1393
1394         psys = context.particle_system
1395         ob = context.object
1396         part = particle_get_settings(context)
1397
1398         col = layout.column()
1399
1400         col.prop(part, "dupli_group")
1401
1402         col.prop(part, "use_whole_group")
1403         sub = col.column()
1404         sub.active = (part.use_whole_group is False)
1405         sub.prop(part, "use_group_pick_random")
1406         sub.prop(part, "use_global_dupli", text="Global Coordinates")
1407         sub.prop(part, "use_rotation_dupli", text="Object Rotation")
1408         sub.prop(part, "use_scale_dupli", text="Object Scale")
1409
1410
1411 class PARTICLE_PT_render_collection_use_count(ParticleButtonsPanel, Panel):
1412     bl_label = "Use Count"
1413     bl_parent_id = "PARTICLE_PT_render_collection"
1414     bl_options = {'DEFAULT_CLOSED'}
1415     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1416
1417     @classmethod
1418     def poll(cls, context):
1419         part = particle_get_settings(context)
1420         return part.render_type == 'COLLECTION'
1421
1422     def draw_header(self, context):
1423         layout = self.layout
1424         part = particle_get_settings(context)
1425
1426         layout.active = not part.use_whole_group
1427
1428         layout.prop(part, "use_group_count", text="")
1429
1430     def draw(self, context):
1431         layout = self.layout
1432         layout.use_property_split = True
1433
1434         psys = context.particle_system
1435         ob = context.object
1436         part = particle_get_settings(context)
1437
1438         col = layout.column()
1439
1440         layout.active = part.use_group_count and not part.use_whole_group
1441
1442         row = layout.row()
1443         row.template_list("UI_UL_list", "particle_dupli_weights", part, "dupli_weights",
1444                           part, "active_dupliweight_index")
1445
1446         col = row.column()
1447         sub = col.row()
1448         subsub = sub.column(align=True)
1449         subsub.operator("particle.dupliob_copy", icon='ZOOMIN', text="")
1450         subsub.operator("particle.dupliob_remove", icon='ZOOMOUT', text="")
1451         subsub.operator("particle.dupliob_move_up", icon='TRIA_UP', text="")
1452         subsub.operator("particle.dupliob_move_down", icon='TRIA_DOWN', text="")
1453         subsub.separator()
1454         subsub.operator("particle.dupliob_refresh", icon='FILE_REFRESH', text="")
1455
1456         weight = part.active_dupliweight
1457         if weight:
1458             row = layout.row()
1459             row.prop(weight, "count")
1460
1461
1462 class PARTICLE_PT_render_billboards_alignment(ParticleButtonsPanel, Panel):
1463     bl_label = "Billboard Alignment"
1464     bl_parent_id = "PARTICLE_PT_render"
1465     bl_options = {'DEFAULT_CLOSED'}
1466     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1467
1468     @classmethod
1469     def poll(cls, context):
1470         part = particle_get_settings(context)
1471         return part.render_type == 'BILLBOARD'
1472
1473     def draw(self, context):
1474         layout = self.layout
1475         layout.use_property_split = True
1476
1477         psys = context.particle_system
1478         ob = context.object
1479         part = particle_get_settings(context)
1480
1481         col = layout.column()
1482
1483         col.prop(part, "billboard_align", text="Align To")
1484         col.prop(part, "lock_billboard", text="Lock Axis")
1485         col.prop(part, "billboard_object")
1486
1487
1488 class PARTICLE_PT_render_billboards_tilt(ParticleButtonsPanel, Panel):
1489     bl_label = "Billboard Tilt"
1490     bl_parent_id = "PARTICLE_PT_render"
1491     bl_options = {'DEFAULT_CLOSED'}
1492     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1493
1494     @classmethod
1495     def poll(cls, context):
1496         part = particle_get_settings(context)
1497         return part.render_type == 'BILLBOARD'
1498
1499     def draw(self, context):
1500         layout = self.layout
1501         layout.use_property_split = True
1502
1503         psys = context.particle_system
1504         ob = context.object
1505         part = particle_get_settings(context)
1506
1507         col = layout.column()
1508
1509         sub = col.column(align=True)
1510         sub.prop(part, "billboard_tilt", text="Angle", slider=True)
1511         sub.prop(part, "billboard_tilt_random", text="Random", slider=True)
1512
1513         sub = col.column(align=True)
1514         sub.prop(part, "billboard_offset")
1515         col.prop(part, "billboard_size", text="Scale")
1516         if part.billboard_align == 'VEL':
1517             col = col.column(align=True)
1518             col.prop(part, "billboard_velocity_head", text="Velocity ScaleHead")
1519             col.prop(part, "billboard_velocity_tail", text="Tail")
1520
1521
1522 class PARTICLE_PT_render_billboards_uv(ParticleButtonsPanel, Panel):
1523     bl_label = "Billboard UVs"
1524     bl_parent_id = "PARTICLE_PT_render"
1525     bl_options = {'DEFAULT_CLOSED'}
1526     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1527
1528     @classmethod
1529     def poll(cls, context):
1530         part = particle_get_settings(context)
1531         return part.render_type == 'BILLBOARD'
1532
1533     def draw(self, context):
1534         layout = self.layout
1535         layout.use_property_split = True
1536
1537         psys = context.particle_system
1538         ob = context.object
1539         part = particle_get_settings(context)
1540
1541         col = layout.column()
1542
1543         if psys:
1544             col.prop_search(psys, "billboard_normal_uv", ob.data, "uv_layers")
1545             col.prop_search(psys, "billboard_time_index_uv", ob.data, "uv_layers")
1546
1547         col.prop(part, "billboard_uv_split", text="Split UVs")
1548
1549         if psys:
1550             sub = col.column()
1551             sub.active = part.billboard_uv_split > 1
1552             sub.prop_search(psys, "billboard_split_uv", ob.data, "uv_layers")
1553
1554         sub.prop(part, "billboard_animation")
1555         sub.prop(part, "billboard_offset_split")
1556
1557
1558 class PARTICLE_PT_render_trails(ParticleButtonsPanel, Panel):
1559     bl_label = "Trails"
1560     bl_parent_id = "PARTICLE_PT_render"
1561     bl_options = {'DEFAULT_CLOSED'}
1562     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1563
1564     @classmethod
1565     def poll(cls, context):
1566         part = particle_get_settings(context)
1567         return part.render_type in {'HALO', 'LINE', 'BILLBOARD'}
1568
1569     def draw(self, context):
1570         layout = self.layout
1571         layout.use_property_split = True
1572
1573         psys = context.particle_system
1574         part = particle_get_settings(context)
1575
1576         col = layout.column()
1577
1578         col.prop(part, "trail_count")
1579
1580         sub = col.column()
1581         sub.active = (part.trail_count > 1)
1582         sub.prop(part, "use_absolute_path_time", text="Length in Frames")
1583         sub.prop(part, "path_end", text="Length", slider=not part.use_absolute_path_time)
1584         sub.prop(part, "length_random", text="Random Length", slider=True)
1585
1586
1587 class PARTICLE_PT_draw(ParticleButtonsPanel, Panel):
1588     bl_label = "Viewport Display"
1589     bl_options = {'DEFAULT_CLOSED'}
1590     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1591
1592     @classmethod
1593     def poll(cls, context):
1594         settings = particle_get_settings(context)
1595         engine = context.engine
1596         if settings is None:
1597             return False
1598         return engine in cls.COMPAT_ENGINES
1599
1600     def draw(self, context):
1601         layout = self.layout
1602         layout.use_property_split = True
1603
1604         psys = context.particle_system
1605         part = particle_get_settings(context)
1606
1607         layout.prop(part, "draw_method", text="Display As")
1608
1609         if part.draw_method == 'NONE' or (part.render_type == 'NONE' and part.draw_method == 'RENDER'):
1610             return
1611
1612         path = (part.render_type == 'PATH' and part.draw_method == 'RENDER') or part.draw_method == 'PATH'
1613
1614         layout.separator()
1615
1616         col = layout.column()
1617         col.prop(part, "draw_color", text="Color")
1618         if part.draw_color in {'VELOCITY', 'ACCELERATION'}:
1619             col.prop(part, "color_maximum", text="Fade Distance")
1620
1621         col = layout.column()
1622
1623         if path:
1624             col.prop(part, "draw_step", text="Strand Steps")
1625         col.prop(part, "draw_percentage", slider=True, text="Amount")
1626         if part.draw_method != 'RENDER' or part.render_type == 'HALO':
1627             col.prop(part, "draw_size", text="Size")
1628
1629         if part.draw_percentage != 100 and psys is not None:
1630             if part.type == 'HAIR':
1631                 if psys.use_hair_dynamics and psys.point_cache.is_baked is False:
1632                     layout.row().label(text="Display percentage makes dynamics inaccurate without baking")
1633             else:
1634                 phystype = part.physics_type
1635                 if phystype != 'NO' and phystype != 'KEYED' and psys.point_cache.is_baked is False:
1636                     layout.row().label(text="Display percentage makes dynamics inaccurate without baking")
1637         else:
1638             layout.row().label(text="")
1639
1640         col = layout.column()
1641         col.prop(part, "show_guide_hairs", text="Guide Hairs")
1642         col.prop(part, "show_size")
1643         col.prop(part, "show_velocity")
1644         col.prop(part, "show_number")
1645         if part.physics_type == 'BOIDS':
1646             col.prop(part, "show_health")
1647
1648
1649 class PARTICLE_PT_children(ParticleButtonsPanel, Panel):
1650     bl_label = "Children"
1651     bl_options = {'DEFAULT_CLOSED'}
1652     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1653
1654     @classmethod
1655     def poll(cls, context):
1656         return particle_panel_poll(cls, context)
1657
1658     def draw(self, context):
1659         layout = self.layout
1660
1661         psys = context.particle_system
1662         part = particle_get_settings(context)
1663
1664         layout.row().prop(part, "child_type", expand=True)
1665
1666         layout.use_property_split = True
1667
1668         if part.child_type == 'NONE':
1669             return
1670
1671         col = layout.column()
1672
1673         sub = col.column(align=True)
1674         sub.prop(part, "child_nbr", text="Display Amount")
1675         sub.prop(part, "rendered_child_count", text="Render Amount")
1676
1677         col.separator()
1678
1679         col.prop(part, "child_length", slider=True)
1680         col.prop(part, "child_length_threshold", slider=True)
1681         if psys:
1682             col.prop(psys, "child_seed", text="Seed")
1683
1684         col.separator()
1685
1686         if part.child_type == 'INTERPOLATED':
1687             col.prop(part, "virtual_parents", slider=True)
1688             col.prop(part, "create_long_hair_children")
1689         else:
1690             col.separator()
1691             sub = col.column(align=True)
1692             sub.prop(part, "child_size", text="Size")
1693             sub.prop(part, "child_size_random", text="Randomize Size", slider=True)
1694
1695         if part.child_type == 'SIMPLE':
1696             col.separator()
1697             col.prop(part, "child_radius", text="Radius")
1698             col.prop(part, "child_roundness", text="Roundness", slider=True)
1699         elif part.virtual_parents > 0.0:
1700             sub = col.column(align=True)
1701             sub.label(text="Parting not available with virtual parents")
1702
1703
1704 class PARTICLE_PT_children_parting(ParticleButtonsPanel, Panel):
1705     bl_label = "Parting"
1706     bl_parent_id = "PARTICLE_PT_children"
1707     bl_options = {'DEFAULT_CLOSED'}
1708     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1709
1710     @classmethod
1711     def poll(cls, context):
1712         part = particle_get_settings(context)
1713         return part.child_type == 'INTERPOLATED'
1714
1715     def draw(self, context):
1716         layout = self.layout
1717
1718         psys = context.particle_system
1719         part = particle_get_settings(context)
1720
1721         layout.use_property_split = True
1722
1723         col = layout.column()
1724         col.prop(part, "child_parting_factor", text="Parting", slider=True)
1725         col.prop(part, "child_parting_min", text="Min")
1726         col.prop(part, "child_parting_max", text="Max")
1727
1728
1729 class PARTICLE_PT_children_clumping(ParticleButtonsPanel, Panel):
1730     bl_label = "Clumping"
1731     bl_parent_id = "PARTICLE_PT_children"
1732     bl_options = {'DEFAULT_CLOSED'}
1733     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1734
1735     @classmethod
1736     def poll(cls, context):
1737         part = particle_get_settings(context)
1738         return part.child_type != 'NONE'
1739
1740     def draw(self, context):
1741         layout = self.layout
1742
1743         psys = context.particle_system
1744         part = particle_get_settings(context)
1745
1746         layout.use_property_split = True
1747
1748         col = layout.column()
1749
1750         sub = col.column()
1751
1752         sub.prop(part, "use_clump_curve")
1753         if part.use_clump_curve:
1754             sub.template_curve_mapping(part, "clump_curve")
1755         else:
1756             sub.prop(part, "clump_factor", slider=True)
1757             sub.prop(part, "clump_shape", slider=True)
1758         sub = col.column(align=True)
1759         sub.prop(part, "use_clump_noise")
1760         subsub = sub.column()
1761         subsub.enabled = part.use_clump_noise
1762         subsub.prop(part, "clump_noise_size")
1763
1764         if part.child_type == 'SIMPLE':
1765             sub.prop(part, "twist")
1766             sub.prop(part, "use_twist_curve")
1767             if part.use_twist_curve:
1768                 sub.template_curve_mapping(part, "twist_curve")
1769
1770
1771 class PARTICLE_PT_children_roughness(ParticleButtonsPanel, Panel):
1772     bl_label = "Roughness"
1773     bl_parent_id = "PARTICLE_PT_children"
1774     bl_options = {'DEFAULT_CLOSED'}
1775     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1776
1777     @classmethod
1778     def poll(cls, context):
1779         part = particle_get_settings(context)
1780         return part.child_type != 'NONE'
1781
1782     def draw(self, context):
1783         layout = self.layout
1784
1785         psys = context.particle_system
1786         part = particle_get_settings(context)
1787
1788         layout.use_property_split = True
1789
1790         col = layout.column()
1791
1792         col.prop(part, "use_roughness_curve")
1793         if part.use_roughness_curve:
1794             sub = col.column()
1795             sub.template_curve_mapping(part, "roughness_curve")
1796             sub.prop(part, "roughness_1", text="Roughness")
1797             sub.prop(part, "roughness_1_size", text="Size")
1798         else:
1799             sub = col.column(align=True)
1800             sub.prop(part, "roughness_1", text="Uniform")
1801             sub.prop(part, "roughness_1_size", text="Size")
1802
1803             sub = col.column(align=True)
1804             sub.prop(part, "roughness_endpoint", text="Endpoint")
1805             sub.prop(part, "roughness_end_shape")
1806
1807             sub = col.column(align=True)
1808             sub.prop(part, "roughness_2", text="Random")
1809             sub.prop(part, "roughness_2_size", text="Size")
1810             sub.prop(part, "roughness_2_threshold", slider=True)
1811
1812
1813 class PARTICLE_PT_children_kink(ParticleButtonsPanel, Panel):
1814     bl_label = "Kink"
1815     bl_parent_id = "PARTICLE_PT_children"
1816     bl_options = {'DEFAULT_CLOSED'}
1817     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1818
1819     @classmethod
1820     def poll(cls, context):
1821         part = particle_get_settings(context)
1822         return part.child_type != 'NONE'
1823
1824     def draw(self, context):
1825         layout = self.layout
1826
1827         psys = context.particle_system
1828         part = particle_get_settings(context)
1829
1830         layout.use_property_split = True
1831
1832         col = layout.column()
1833
1834         col.prop(part, "kink", text="Kink Type")
1835         col = layout.column()
1836         col.active = part.kink != 'NO'
1837
1838         if part.kink == 'SPIRAL':
1839
1840             sub = col.column()
1841             sub.prop(part, "kink_amplitude", text="Amplitude")
1842             sub.prop(part, "kink_amplitude_random", text="Randomize Amplitude", slider=True)
1843
1844             col.separator()
1845
1846             sub = col.column()
1847             sub.prop(part, "kink_axis")
1848             sub.prop(part, "kink_axis_random", text="Randomize Axis", slider=True)
1849
1850             col.separator()
1851
1852             col.prop(part, "kink_frequency", text="Frequency")
1853             col.prop(part, "kink_shape", text="Shape", slider=True)
1854             col.prop(part, "kink_extra_steps", text="Steps")
1855
1856         elif part.kink in {'CURL', 'RADIAL', 'WAVE', 'BRAID', 'WAVE'}:
1857             sub = col.column(align=True)
1858             sub.prop(part, "kink_amplitude")
1859             sub.prop(part, "kink_amplitude_clump", text="Clump", slider=True)
1860             col.prop(part, "kink_flat", slider=True)
1861             col.prop(part, "kink_frequency")
1862             col.prop(part, "kink_shape", slider=True)
1863
1864
1865 class PARTICLE_PT_field_weights(ParticleButtonsPanel, Panel):
1866     bl_label = "Field Weights"
1867     bl_options = {'DEFAULT_CLOSED'}
1868     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1869
1870     @classmethod
1871     def poll(cls, context):
1872         return particle_panel_poll(cls, context)
1873
1874     def draw(self, context):
1875         part = particle_get_settings(context)
1876         effector_weights_ui(self, context, part.effector_weights, 'PSYS')
1877
1878         if part.type == 'HAIR':
1879             row = self.layout.row()
1880             row.prop(part.effector_weights, "apply_to_hair_growing")
1881             row.prop(part, "apply_effector_to_children")
1882             row = self.layout.row()
1883             row.prop(part, "effect_hair", slider=True)
1884
1885
1886 class PARTICLE_PT_force_fields(ParticleButtonsPanel, Panel):
1887     bl_label = "Force Field Settings"
1888     bl_options = {'DEFAULT_CLOSED'}
1889     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1890
1891     def draw(self, context):
1892         layout = self.layout
1893         layout.use_property_split = True
1894
1895         part = particle_get_settings(context)
1896
1897         col = layout.column()
1898         col.prop(part, "use_self_effect")
1899         col.prop(part, "effector_amount", text="Effector Amount")
1900
1901
1902 class PARTICLE_PT_force_fields_type1(ParticleButtonsPanel, Panel):
1903     bl_label = "Type 1"
1904     bl_parent_id = "PARTICLE_PT_force_fields"
1905     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1906
1907     def draw(self, context):
1908         layout = self.layout
1909         layout.use_property_split = True
1910
1911         part = particle_get_settings(context)
1912
1913         col = layout.column()
1914         col.prop(part.force_field_1, "type", text="Type 1")
1915         basic_force_field_settings_ui(self, context, part.force_field_1)
1916
1917
1918 class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
1919     bl_label = "Type 2"
1920     bl_parent_id = "PARTICLE_PT_force_fields"
1921     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1922
1923     def draw(self, context):
1924         layout = self.layout
1925         layout.use_property_split = True
1926
1927         part = particle_get_settings(context)
1928
1929         col = layout.column()
1930         col.prop(part.force_field_2, "type", text="Type 2")
1931         basic_force_field_settings_ui(self, context, part.force_field_2)
1932
1933
1934 class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
1935     bl_label = "Falloff"
1936     bl_options = {'DEFAULT_CLOSED'}
1937     bl_parent_id = "PARTICLE_PT_force_fields_type1"
1938     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1939
1940     def draw(self, context):
1941         layout = self.layout
1942         layout.use_property_split = True
1943
1944         part = particle_get_settings(context)
1945
1946         basic_force_field_falloff_ui(self, context, part.force_field_1)
1947
1948
1949 class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
1950     bl_label = "Falloff"
1951     bl_options = {'DEFAULT_CLOSED'}
1952     bl_parent_id = "PARTICLE_PT_force_fields_type2"
1953     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1954
1955     def draw(self, context):
1956         layout = self.layout
1957         layout.use_property_split = True
1958
1959         part = particle_get_settings(context)
1960
1961         basic_force_field_falloff_ui(self, context, part.force_field_2)
1962
1963
1964 class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):
1965     bl_label = "Vertex Groups"
1966     bl_options = {'DEFAULT_CLOSED'}
1967     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
1968
1969     @classmethod
1970     def poll(cls, context):
1971         if context.particle_system is None:
1972             return False
1973         return particle_panel_poll(cls, context)
1974
1975     def draw(self, context):
1976         layout = self.layout
1977         layout.use_property_split = True
1978
1979         ob = context.object
1980         psys = context.particle_system
1981
1982         col = layout.column()
1983         row = col.row(align=True)
1984         row.prop_search(psys, "vertex_group_density", ob, "vertex_groups", text="Density")
1985         row.prop(psys, "invert_vertex_group_density", text="", toggle=True, icon='ARROW_LEFTRIGHT')
1986
1987         row = col.row(align=True)
1988         row.prop_search(psys, "vertex_group_length", ob, "vertex_groups", text="Length")
1989         row.prop(psys, "invert_vertex_group_length", text="", toggle=True, icon='ARROW_LEFTRIGHT')
1990
1991         row = col.row(align=True)
1992         row.prop_search(psys, "vertex_group_clump", ob, "vertex_groups", text="Clump")
1993         row.prop(psys, "invert_vertex_group_clump", text="", toggle=True, icon='ARROW_LEFTRIGHT')
1994
1995         row = col.row(align=True)
1996         row.prop_search(psys, "vertex_group_kink", ob, "vertex_groups", text="Kink")
1997         row.prop(psys, "invert_vertex_group_kink", text="", toggle=True, icon='ARROW_LEFTRIGHT')
1998
1999         row = col.row(align=True)
2000         row.prop_search(psys, "vertex_group_roughness_1", ob, "vertex_groups", text="Roughness 1")
2001         row.prop(psys, "invert_vertex_group_roughness_1", text="", toggle=True, icon='ARROW_LEFTRIGHT')
2002
2003         row = col.row(align=True)
2004         row.prop_search(psys, "vertex_group_roughness_2", ob, "vertex_groups", text="Roughness 2")
2005         row.prop(psys, "invert_vertex_group_roughness_2", text="", toggle=True, icon='ARROW_LEFTRIGHT')
2006
2007         row = col.row(align=True)
2008         row.prop_search(psys, "vertex_group_roughness_end", ob, "vertex_groups", text="Roughness End")
2009         row.prop(psys, "invert_vertex_group_roughness_end", text="", toggle=True, icon='ARROW_LEFTRIGHT')
2010
2011         row = col.row(align=True)
2012         row.prop_search(psys, "vertex_group_twist", ob, "vertex_groups", text="Twist")
2013         row.prop(psys, "invert_vertex_group_twist", text="", toggle=True, icon='ARROW_LEFTRIGHT')
2014
2015         # Commented out vertex groups don't work and are still waiting for better implementation
2016         # row = layout.row()
2017         # row.prop_search(psys, "vertex_group_velocity", ob, "vertex_groups", text="Velocity")
2018         # row.prop(psys, "invert_vertex_group_velocity", text="")
2019
2020         # row = layout.row()
2021         # row.prop_search(psys, "vertex_group_size", ob, "vertex_groups", text="Size")
2022         # row.prop(psys, "invert_vertex_group_size", text="")
2023
2024         # row = layout.row()
2025         # row.prop_search(psys, "vertex_group_tangent", ob, "vertex_groups", text="Tangent")
2026         # row.prop(psys, "invert_vertex_group_tangent", text="")
2027
2028         # row = layout.row()
2029         # row.prop_search(psys, "vertex_group_rotation", ob, "vertex_groups", text="Rotation")
2030         # row.prop(psys, "invert_vertex_group_rotation", text="")
2031
2032         # row = layout.row()
2033         # row.prop_search(psys, "vertex_group_field", ob, "vertex_groups", text="Field")
2034         # row.prop(psys, "invert_vertex_group_field", text="")
2035
2036
2037 class PARTICLE_PT_textures(ParticleButtonsPanel, Panel):
2038     bl_label = "Textures"
2039     bl_options = {'DEFAULT_CLOSED'}
2040     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
2041
2042     @classmethod
2043     def poll(cls, context):
2044         if context.particle_system is None:
2045             return False
2046         return particle_panel_poll(cls, context)
2047
2048     def draw(self, context):
2049         layout = self.layout
2050
2051         psys = context.particle_system
2052         part = psys.settings
2053
2054         row = layout.row()
2055         row.template_list("TEXTURE_UL_texslots", "", part, "texture_slots", part, "active_texture_index", rows=2)
2056
2057         col = row.column(align=True)
2058         col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP'
2059         col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN'
2060         col.menu("TEXTURE_MT_specials", icon='DOWNARROW_HLT', text="")
2061
2062         if not part.active_texture:
2063             layout.template_ID(part, "active_texture", new="texture.new")
2064         else:
2065             slot = part.texture_slots[part.active_texture_index]
2066             layout.template_ID(slot, "texture", new="texture.new")
2067
2068
2069 class PARTICLE_PT_hair_shape(ParticleButtonsPanel, Panel):
2070     bl_label = "Hair Shape"
2071     bl_options = {'DEFAULT_CLOSED'}
2072     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
2073
2074     @classmethod
2075     def poll(cls, context):
2076         if context.particle_system is None:
2077             return False
2078         return particle_panel_poll(cls, context)
2079
2080     def draw(self, context):
2081         layout = self.layout
2082         layout.use_property_split = True
2083
2084         psys = context.particle_system
2085         part = psys.settings
2086
2087         layout.prop(part, "shape", text="Strand Shape")
2088
2089         col = layout.column(align=True)
2090         col.prop(part, "root_radius", text="Radius Root")
2091         col.prop(part, "tip_radius", text="Tip")
2092
2093         col = layout.column()
2094         col.prop(part, "radius_scale", text="Radius Scaling")
2095         col.prop(part, "use_close_tip")
2096
2097
2098 class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel, Panel):
2099     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
2100     _context_path = "particle_system.settings"
2101     _property_type = bpy.types.ParticleSettings
2102
2103
2104 classes = (
2105     PARTICLE_MT_specials,
2106     PARTICLE_PT_hair_dynamics_presets,
2107     PARTICLE_UL_particle_systems,
2108     PARTICLE_PT_context_particles,
2109     PARTICLE_PT_emission,
2110     PARTICLE_PT_emission_source,
2111     PARTICLE_PT_hair_dynamics,
2112     PARTICLE_PT_hair_dynamics_structure,
2113     PARTICLE_PT_hair_dynamics_volume,
2114     PARTICLE_PT_cache,
2115     PARTICLE_PT_velocity,
2116     PARTICLE_PT_rotation,
2117     PARTICLE_PT_rotation_angular_velocity,
2118     PARTICLE_PT_physics,
2119     PARTICLE_PT_physics_fluid_springs,
2120     PARTICLE_PT_physics_fluid_springs_viscoelastic,
2121     PARTICLE_PT_physics_fluid_springs_advanced,
2122     PARTICLE_PT_physics_fluid_advanced,
2123     PARTICLE_PT_physics_boids_movement,
2124     PARTICLE_PT_physics_boids_battle,
2125     PARTICLE_PT_physics_boids_misc,
2126     PARTICLE_PT_physics_forces,
2127     PARTICLE_PT_physics_deflection,
2128     PARTICLE_PT_physics_integration,
2129     PARTICLE_PT_physics_relations,
2130     PARTICLE_PT_boidbrain,
2131     PARTICLE_PT_render,
2132     PARTICLE_PT_render_line,
2133     PARTICLE_PT_render_path,
2134     PARTICLE_PT_render_path_timing,
2135     PARTICLE_PT_render_object,
2136     PARTICLE_PT_render_collection,
2137     PARTICLE_PT_render_collection_use_count,
2138     PARTICLE_PT_render_billboards_tilt,
2139     PARTICLE_PT_render_billboards_uv,
2140     PARTICLE_PT_render_trails,
2141     PARTICLE_PT_render_extra,
2142     PARTICLE_PT_draw,
2143     PARTICLE_PT_children,
2144     PARTICLE_PT_children_parting,
2145     PARTICLE_PT_children_clumping,
2146     PARTICLE_PT_children_roughness,
2147     PARTICLE_PT_children_kink,
2148     PARTICLE_PT_hair_shape,
2149     PARTICLE_PT_field_weights,
2150     PARTICLE_PT_force_fields,
2151     PARTICLE_PT_force_fields_type1,
2152     PARTICLE_PT_force_fields_type1_falloff,
2153     PARTICLE_PT_force_fields_type2,
2154     PARTICLE_PT_force_fields_type2_falloff,
2155     PARTICLE_PT_vertexgroups,
2156     PARTICLE_PT_textures,
2157     PARTICLE_PT_custom_props,
2158 )
2159
2160 if __name__ == "__main__":  # only for live edit.
2161     from bpy.utils import register_class
2162     for cls in classes:
2163         register_class(cls)