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