cleanup for addon python internals, fix filtering bug.
[blender.git] / release / scripts / 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 rna_prop_ui import PropertyPanel
22
23 from properties_physics_common import point_cache_ui
24 from properties_physics_common import effector_weights_ui
25 from properties_physics_common import basic_force_field_settings_ui
26 from properties_physics_common import basic_force_field_falloff_ui
27
28
29 def particle_panel_enabled(context, psys):
30     return (psys.point_cache.baked is False) and (not psys.edited) and (not context.particle_system_editable)
31
32
33 def particle_panel_poll(context):
34     psys = context.particle_system
35     if psys is None:
36         return False
37     if psys.settings is None:
38         return False
39     return psys.settings.type in ('EMITTER', 'REACTOR', 'HAIR')
40
41
42 class ParticleButtonsPanel(bpy.types.Panel):
43     bl_space_type = 'PROPERTIES'
44     bl_region_type = 'WINDOW'
45     bl_context = "particle"
46
47     def poll(self, context):
48         return particle_panel_poll(context)
49
50
51 class PARTICLE_PT_context_particles(ParticleButtonsPanel):
52     bl_label = ""
53     bl_show_header = False
54
55     def poll(self, context):
56         return (context.particle_system or context.object)
57
58     def draw(self, context):
59         layout = self.layout
60
61         ob = context.object
62         psys = context.particle_system
63
64         if ob:
65             row = layout.row()
66
67             row.template_list(ob, "particle_systems", ob, "active_particle_system_index", rows=2)
68
69             col = row.column(align=True)
70             col.operator("object.particle_system_add", icon='ZOOMIN', text="")
71             col.operator("object.particle_system_remove", icon='ZOOMOUT', text="")
72
73         if psys and not psys.settings:
74             split = layout.split(percentage=0.32)
75
76             col = split.column()
77             col.label(text="Name:")
78             col.label(text="Settings:")
79
80             col = split.column()
81             col.prop(psys, "name", text="")
82             col.template_ID(psys, "settings", new="particle.new")
83         elif psys:
84             part = psys.settings
85
86             split = layout.split(percentage=0.32)
87             col = split.column()
88             col.label(text="Name:")
89             if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
90                 col.label(text="Settings:")
91                 col.label(text="Type:")
92
93             col = split.column()
94             col.prop(psys, "name", text="")
95             if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
96                 col.template_ID(psys, "settings", new="particle.new")
97
98             #row = layout.row()
99             #row.label(text="Viewport")
100             #row.label(text="Render")
101
102             if part:
103                 if part.type not in ('EMITTER', 'REACTOR', 'HAIR'):
104                     layout.label(text="No settings for fluid particles")
105                     return
106
107                 row = col.row()
108                 row.enabled = particle_panel_enabled(context, psys)
109                 row.prop(part, "type", text="")
110                 row.prop(psys, "seed")
111
112                 split = layout.split(percentage=0.65)
113                 if part.type == 'HAIR':
114                     if psys.edited:
115                         split.operator("particle.edited_clear", text="Free Edit")
116                     else:
117                         split.label(text="")
118                     row = split.row()
119                     row.enabled = particle_panel_enabled(context, psys)
120                     row.prop(part, "hair_step")
121                     if psys.edited:
122                         if psys.global_hair:
123                             layout.operator("particle.connect_hair")
124                             layout.label(text="Hair is disconnected.")
125                         else:
126                             layout.operator("particle.disconnect_hair")
127                             layout.label(text="")
128                 elif part.type == 'REACTOR':
129                     split.enabled = particle_panel_enabled(context, psys)
130                     split.prop(psys, "reactor_target_object")
131                     split.prop(psys, "reactor_target_particle_system", text="Particle System")
132
133
134 class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel):
135     _context_path = "particle_system.settings"
136
137
138 class PARTICLE_PT_emission(ParticleButtonsPanel):
139     bl_label = "Emission"
140
141     def poll(self, context):
142         if particle_panel_poll(context):
143             return not context.particle_system.point_cache.external
144         else:
145             return False
146
147     def draw(self, context):
148         layout = self.layout
149
150         psys = context.particle_system
151         part = psys.settings
152
153         layout.enabled = particle_panel_enabled(context, psys) and not psys.multiple_caches
154
155         row = layout.row()
156         row.active = part.distribution != 'GRID'
157         row.prop(part, "amount")
158
159         if part.type != 'HAIR':
160             split = layout.split()
161
162             col = split.column(align=True)
163             col.prop(part, "start")
164             col.prop(part, "end")
165
166             col = split.column(align=True)
167             col.prop(part, "lifetime")
168             col.prop(part, "random_lifetime", slider=True)
169
170         layout.row().label(text="Emit From:")
171
172         row = layout.row()
173         row.prop(part, "emit_from", expand=True)
174         row = layout.row()
175         row.prop(part, "trand")
176         if part.distribution != 'GRID':
177             row.prop(part, "even_distribution")
178
179         if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
180             row = layout.row()
181             row.prop(part, "distribution", expand=True)
182
183             row = layout.row()
184
185             if part.distribution == 'JIT':
186                 row.prop(part, "userjit", text="Particles/Face")
187                 row.prop(part, "jitter_factor", text="Jittering Amount", slider=True)
188             elif part.distribution == 'GRID':
189                 row.prop(part, "grid_resolution")
190
191
192 class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel):
193     bl_label = "Hair dynamics"
194     bl_default_closed = True
195
196     def poll(self, context):
197         psys = context.particle_system
198         if psys is None:
199             return False
200         if psys.settings is None:
201             return False
202         return psys.settings.type == 'HAIR'
203
204     def draw_header(self, context):
205         #cloth = context.cloth.collision_settings
206
207         #self.layout.active = cloth_panel_enabled(context.cloth)
208         #self.layout.prop(cloth, "enable_collision", text="")
209         psys = context.particle_system
210         self.layout.prop(psys, "hair_dynamics", text="")
211
212     def draw(self, context):
213         layout = self.layout
214
215         psys = context.particle_system
216
217         if not psys.cloth:
218             return
219
220         #part = psys.settings
221         cloth = psys.cloth.settings
222
223         layout.enabled = psys.hair_dynamics
224
225         split = layout.split()
226
227         col = split.column()
228         col.label(text="Material:")
229         sub = col.column(align=True)
230         sub.prop(cloth, "pin_stiffness", text="Stiffness")
231         sub.prop(cloth, "mass")
232         sub.prop(cloth, "bending_stiffness", text="Bending")
233         sub.prop(cloth, "internal_friction", slider=True)
234         sub.prop(cloth, "collider_friction", slider=True)
235
236         col = split.column()
237
238         col.label(text="Damping:")
239         sub = col.column(align=True)
240         sub.prop(cloth, "spring_damping", text="Spring")
241         sub.prop(cloth, "air_damping", text="Air")
242
243         col.label(text="Quality:")
244         col.prop(cloth, "quality", text="Steps", slider=True)
245
246
247 class PARTICLE_PT_cache(ParticleButtonsPanel):
248     bl_label = "Cache"
249     bl_default_closed = True
250
251     def poll(self, context):
252         psys = context.particle_system
253         if psys is None:
254             return False
255         if psys.settings is None:
256             return False
257         phystype = psys.settings.physics_type
258         if phystype == 'NO' or phystype == 'KEYED':
259             return False
260         return psys.settings.type in ('EMITTER', 'REACTOR') or (psys.settings.type == 'HAIR' and psys.hair_dynamics)
261
262     def draw(self, context):
263
264         psys = context.particle_system
265
266         point_cache_ui(self, context, psys.point_cache, particle_panel_enabled(context, psys), not psys.hair_dynamics, 0)
267
268
269 class PARTICLE_PT_velocity(ParticleButtonsPanel):
270     bl_label = "Velocity"
271
272     def poll(self, context):
273         if particle_panel_poll(context):
274             psys = context.particle_system
275             return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.external
276         else:
277             return False
278
279     def draw(self, context):
280         layout = self.layout
281
282         psys = context.particle_system
283         part = psys.settings
284
285         layout.enabled = particle_panel_enabled(context, psys)
286
287         split = layout.split()
288
289         sub = split.column()
290         sub.label(text="Emitter Geometry:")
291         sub.prop(part, "normal_factor")
292         subsub = sub.column(align=True)
293         subsub.prop(part, "tangent_factor")
294         subsub.prop(part, "tangent_phase", slider=True)
295
296         sub = split.column()
297         sub.label(text="Emitter Object")
298         sub.prop(part, "object_aligned_factor", text="")
299
300         layout.row().label(text="Other:")
301         split = layout.split()
302         sub = split.column()
303         if part.emit_from == 'PARTICLE':
304             sub.prop(part, "particle_factor")
305         else:
306             sub.prop(part, "object_factor", slider=True)
307         sub = split.column()
308         sub.prop(part, "random_factor")
309
310         #if part.type=='REACTOR':
311         #    sub.prop(part, "reactor_factor")
312         #    sub.prop(part, "reaction_shape", slider=True)
313
314
315 class PARTICLE_PT_rotation(ParticleButtonsPanel):
316     bl_label = "Rotation"
317
318     def poll(self, context):
319         if particle_panel_poll(context):
320             psys = context.particle_system
321             return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.external
322         else:
323             return False
324
325     def draw(self, context):
326         layout = self.layout
327
328         psys = context.particle_system
329         part = psys.settings
330
331         layout.enabled = particle_panel_enabled(context, psys)
332
333         split = layout.split()
334         split.label(text="Initial Rotation:")
335         split.prop(part, "rotation_dynamic")
336         split = layout.split()
337
338         sub = split.column(align=True)
339         sub.prop(part, "rotation_mode", text="")
340         sub.prop(part, "random_rotation_factor", slider=True, text="Random")
341
342         sub = split.column(align=True)
343         sub.prop(part, "phase_factor", slider=True)
344         sub.prop(part, "random_phase_factor", text="Random", slider=True)
345
346         layout.row().label(text="Angular Velocity:")
347         layout.row().prop(part, "angular_velocity_mode", expand=True)
348         split = layout.split()
349
350         sub = split.column()
351
352         if part.angular_velocity_mode != 'NONE':
353             sub.prop(part, "angular_velocity_factor", text="")
354
355
356 class PARTICLE_PT_physics(ParticleButtonsPanel):
357     bl_label = "Physics"
358
359     def poll(self, context):
360         if particle_panel_poll(context):
361             return not context.particle_system.point_cache.external
362         else:
363             return False
364
365     def draw(self, context):
366         layout = self.layout
367
368         psys = context.particle_system
369         part = psys.settings
370
371         layout.enabled = particle_panel_enabled(context, psys)
372
373         row = layout.row()
374         row.prop(part, "physics_type", expand=True)
375         if part.physics_type != 'NO':
376             row = layout.row()
377             col = row.column(align=True)
378             col.prop(part, "particle_size")
379             col.prop(part, "random_size", slider=True)
380             col = row.column(align=True)
381             col.prop(part, "mass")
382             col.prop(part, "sizemass", text="Multiply mass with size")
383
384         if part.physics_type == 'NEWTON':
385             split = layout.split()
386             sub = split.column()
387
388             sub.label(text="Forces:")
389             sub.prop(part, "brownian_factor")
390             sub.prop(part, "drag_factor", slider=True)
391             sub.prop(part, "damp_factor", slider=True)
392             sub = split.column()
393             sub.prop(part, "size_deflect")
394             sub.prop(part, "die_on_collision")
395             sub.prop(part, "integrator")
396             sub.prop(part, "time_tweak")
397
398         elif part.physics_type == 'KEYED':
399             split = layout.split()
400             sub = split.column()
401
402             row = layout.row()
403             col = row.column()
404             col.active = not psys.keyed_timing
405             col.prop(part, "keyed_loops", text="Loops")
406             row.prop(psys, "keyed_timing", text="Use Timing")
407
408             layout.label(text="Keys:")
409         elif part.physics_type == 'BOIDS':
410             boids = part.boids
411
412
413             row = layout.row()
414             row.prop(boids, "allow_flight")
415             row.prop(boids, "allow_land")
416             row.prop(boids, "allow_climb")
417
418             split = layout.split()
419
420             sub = split.column()
421             col = sub.column(align=True)
422             col.active = boids.allow_flight
423             col.prop(boids, "air_max_speed")
424             col.prop(boids, "air_min_speed", slider=True)
425             col.prop(boids, "air_max_acc", slider=True)
426             col.prop(boids, "air_max_ave", slider=True)
427             col.prop(boids, "air_personal_space")
428             row = col.row()
429             row.active = (boids.allow_land or boids.allow_climb) and boids.allow_flight
430             row.prop(boids, "landing_smoothness")
431
432             sub = split.column()
433             col = sub.column(align=True)
434             col.active = boids.allow_land or boids.allow_climb
435             col.prop(boids, "land_max_speed")
436             col.prop(boids, "land_jump_speed")
437             col.prop(boids, "land_max_acc", slider=True)
438             col.prop(boids, "land_max_ave", slider=True)
439             col.prop(boids, "land_personal_space")
440             col.prop(boids, "land_stick_force")
441
442             row = layout.row()
443
444             col = row.column(align=True)
445             col.label(text="Battle:")
446             col.prop(boids, "health")
447             col.prop(boids, "strength")
448             col.prop(boids, "aggression")
449             col.prop(boids, "accuracy")
450             col.prop(boids, "range")
451
452             col = row.column()
453             col.label(text="Misc:")
454             col.prop(boids, "banking", slider=True)
455             col.prop(boids, "height", slider=True)
456
457         if part.physics_type == 'KEYED' or part.physics_type == 'BOIDS':
458             if part.physics_type == 'BOIDS':
459                 layout.label(text="Relations:")
460
461             row = layout.row()
462             row.template_list(psys, "targets", psys, "active_particle_target_index")
463
464             col = row.column()
465             sub = col.row()
466             subsub = sub.column(align=True)
467             subsub.operator("particle.new_target", icon='ZOOMIN', text="")
468             subsub.operator("particle.target_remove", icon='ZOOMOUT', text="")
469             sub = col.row()
470             subsub = sub.column(align=True)
471             subsub.operator("particle.target_move_up", icon='MOVE_UP_VEC', text="")
472             subsub.operator("particle.target_move_down", icon='MOVE_DOWN_VEC', text="")
473
474             key = psys.active_particle_target
475             if key:
476                 row = layout.row()
477                 if part.physics_type == 'KEYED':
478                     col = row.column()
479                     #doesn't work yet
480                     #col.red_alert = key.valid
481                     col.prop(key, "object", text="")
482                     col.prop(key, "system", text="System")
483                     col = row.column()
484                     col.active = psys.keyed_timing
485                     col.prop(key, "time")
486                     col.prop(key, "duration")
487                 else:
488                     sub = row.row()
489                     #doesn't work yet
490                     #sub.red_alert = key.valid
491                     sub.prop(key, "object", text="")
492                     sub.prop(key, "system", text="System")
493
494                     layout.prop(key, "mode", expand=True)
495
496
497 class PARTICLE_PT_boidbrain(ParticleButtonsPanel):
498     bl_label = "Boid Brain"
499
500     def poll(self, context):
501         psys = context.particle_system
502         if psys is None:
503             return False
504         if psys.settings is None:
505             return False
506         if psys.point_cache.external:
507             return False
508         return psys.settings.physics_type == 'BOIDS'
509
510     def draw(self, context):
511         layout = self.layout
512
513         boids = context.particle_system.settings.boids
514
515         layout.enabled = particle_panel_enabled(context, context.particle_system)
516
517         # Currently boids can only use the first state so these are commented out for now.
518         #row = layout.row()
519         #row.template_list(boids, "states", boids, "active_boid_state_index", compact="True")
520         #col = row.row()
521         #sub = col.row(align=True)
522         #sub.operator("boid.state_add", icon='ZOOMIN', text="")
523         #sub.operator("boid.state_del", icon='ZOOMOUT', text="")
524         #sub = row.row(align=True)
525         #sub.operator("boid.state_move_up", icon='MOVE_UP_VEC', text="")
526         #sub.operator("boid.state_move_down", icon='MOVE_DOWN_VEC', text="")
527
528         state = boids.active_boid_state
529
530         #layout.prop(state, "name", text="State name")
531
532         row = layout.row()
533         row.prop(state, "ruleset_type")
534         if state.ruleset_type == 'FUZZY':
535             row.prop(state, "rule_fuzziness", slider=True)
536         else:
537             row.label(text="")
538
539         row = layout.row()
540         row.template_list(state, "rules", state, "active_boid_rule_index")
541
542         col = row.column()
543         sub = col.row()
544         subsub = sub.column(align=True)
545         subsub.operator_menu_enum("boid.rule_add", "type", icon='ZOOMIN', text="")
546         subsub.operator("boid.rule_del", icon='ZOOMOUT', text="")
547         sub = col.row()
548         subsub = sub.column(align=True)
549         subsub.operator("boid.rule_move_up", icon='MOVE_UP_VEC', text="")
550         subsub.operator("boid.rule_move_down", icon='MOVE_DOWN_VEC', text="")
551
552         rule = state.active_boid_rule
553
554         if rule:
555             row = layout.row()
556             row.prop(rule, "name", text="")
557             #somebody make nice icons for boids here please! -jahka
558             row.prop(rule, "in_air", icon='MOVE_UP_VEC', text="")
559             row.prop(rule, "on_land", icon='MOVE_DOWN_VEC', text="")
560
561             row = layout.row()
562
563             if rule.type == 'GOAL':
564                 row.prop(rule, "object")
565                 row = layout.row()
566                 row.prop(rule, "predict")
567             elif rule.type == 'AVOID':
568                 row.prop(rule, "object")
569                 row = layout.row()
570                 row.prop(rule, "predict")
571                 row.prop(rule, "fear_factor")
572             elif rule.type == 'FOLLOW_PATH':
573                 row.label(text="Not yet functional.")
574             elif rule.type == 'AVOID_COLLISION':
575                 row.prop(rule, "boids")
576                 row.prop(rule, "deflectors")
577                 row.prop(rule, "look_ahead")
578             elif rule.type == 'FOLLOW_LEADER':
579                 row.prop(rule, "object", text="")
580                 row.prop(rule, "distance")
581                 row = layout.row()
582                 row.prop(rule, "line")
583                 sub = row.row()
584                 sub.active = rule.line
585                 sub.prop(rule, "queue_size")
586             elif rule.type == 'AVERAGE_SPEED':
587                 row.prop(rule, "speed", slider=True)
588                 row.prop(rule, "wander", slider=True)
589                 row.prop(rule, "level", slider=True)
590             elif rule.type == 'FIGHT':
591                 row.prop(rule, "distance")
592                 row.prop(rule, "flee_distance")
593
594
595 class PARTICLE_PT_render(ParticleButtonsPanel):
596     bl_label = "Render"
597
598     def poll(self, context):
599         psys = context.particle_system
600         if psys is None:
601             return False
602         if psys.settings is None:
603             return False
604         return True
605
606     def draw(self, context):
607         layout = self.layout
608
609         psys = context.particle_system
610         part = psys.settings
611
612         row = layout.row()
613         row.prop(part, "material")
614         row.prop(psys, "parent")
615
616         split = layout.split()
617
618         sub = split.column()
619         sub.prop(part, "emitter")
620         sub.prop(part, "parent")
621         sub = split.column()
622         sub.prop(part, "unborn")
623         sub.prop(part, "died")
624
625         row = layout.row()
626         row.prop(part, "ren_as", expand=True)
627
628         split = layout.split()
629
630         sub = split.column()
631
632         if part.ren_as == 'LINE':
633             sub.prop(part, "line_length_tail")
634             sub.prop(part, "line_length_head")
635             sub = split.column()
636             sub.prop(part, "velocity_length")
637         elif part.ren_as == 'PATH':
638
639             if part.type != 'HAIR' and part.physics_type != 'KEYED' and (psys.point_cache.baked is False):
640                 box = layout.box()
641                 box.label(text="Baked or keyed particles needed for correct rendering.")
642                 return
643
644             sub.prop(part, "render_strand")
645             subsub = sub.column()
646             subsub.active = (part.render_strand is False)
647             subsub.prop(part, "render_adaptive")
648             subsub = sub.column()
649             subsub.active = part.render_adaptive or part.render_strand == True
650             subsub.prop(part, "adaptive_angle")
651             subsub = sub.column()
652             subsub.active = (part.render_adaptive is True and part.render_strand is False)
653             subsub.prop(part, "adaptive_pix")
654             sub.prop(part, "hair_bspline")
655             sub.prop(part, "render_step", text="Steps")
656
657             sub = split.column()
658             sub.label(text="Timing:")
659             sub.prop(part, "abs_path_time")
660             sub.prop(part, "path_start", text="Start", slider=not part.abs_path_time)
661             sub.prop(part, "path_end", text="End", slider=not part.abs_path_time)
662             sub.prop(part, "random_length", text="Random", slider=True)
663
664             row = layout.row()
665             col = row.column()
666
667             if part.type == 'HAIR' and part.render_strand == True and part.child_type == 'FACES':
668                 layout.prop(part, "enable_simplify")
669                 if part.enable_simplify == True:
670                     row = layout.row()
671                     row.prop(part, "simplify_refsize")
672                     row.prop(part, "simplify_rate")
673                     row.prop(part, "simplify_transition")
674                     row = layout.row()
675                     row.prop(part, "viewport")
676                     sub = row.row()
677                     sub.active = part.viewport == True
678                     sub.prop(part, "simplify_viewport")
679
680         elif part.ren_as == 'OBJECT':
681             sub.prop(part, "dupli_object")
682             sub.prop(part, "use_global_dupli")
683         elif part.ren_as == 'GROUP':
684             sub.prop(part, "dupli_group")
685             split = layout.split()
686             sub = split.column()
687             sub.prop(part, "whole_group")
688             subsub = sub.column()
689             subsub.active = (part.whole_group is False)
690             subsub.prop(part, "use_group_count")
691
692             sub = split.column()
693             subsub = sub.column()
694             subsub.active = (part.whole_group is False)
695             subsub.prop(part, "use_global_dupli")
696             subsub.prop(part, "rand_group")
697
698             if part.use_group_count and not part.whole_group:
699                 row = layout.row()
700                 row.template_list(part, "dupliweights", part, "active_dupliweight_index")
701
702                 col = row.column()
703                 sub = col.row()
704                 subsub = sub.column(align=True)
705                 subsub.operator("particle.dupliob_copy", icon='ZOOMIN', text="")
706                 subsub.operator("particle.dupliob_remove", icon='ZOOMOUT', text="")
707                 subsub.operator("particle.dupliob_move_up", icon='MOVE_UP_VEC', text="")
708                 subsub.operator("particle.dupliob_move_down", icon='MOVE_DOWN_VEC', text="")
709
710                 weight = part.active_dupliweight
711                 if weight:
712                     row = layout.row()
713                     row.prop(weight, "count")
714
715         elif part.ren_as == 'BILLBOARD':
716             sub.label(text="Align:")
717
718             row = layout.row()
719             row.prop(part, "billboard_align", expand=True)
720             row.prop(part, "billboard_lock", text="Lock")
721             row = layout.row()
722             row.prop(part, "billboard_object")
723
724             row = layout.row()
725             col = row.column(align=True)
726             col.label(text="Tilt:")
727             col.prop(part, "billboard_tilt", text="Angle", slider=True)
728             col.prop(part, "billboard_random_tilt", slider=True)
729             col = row.column()
730             col.prop(part, "billboard_offset")
731
732             row = layout.row()
733             row.prop(psys, "billboard_normal_uv")
734             row = layout.row()
735             row.prop(psys, "billboard_time_index_uv")
736
737             row = layout.row()
738             row.label(text="Split uv's:")
739             row.prop(part, "billboard_uv_split", text="Number of splits")
740             row = layout.row()
741             row.prop(psys, "billboard_split_uv")
742             row = layout.row()
743             row.label(text="Animate:")
744             row.prop(part, "billboard_animation", expand=True)
745             row.label(text="Offset:")
746             row.prop(part, "billboard_split_offset", expand=True)
747
748         if part.ren_as == 'HALO' or part.ren_as == 'LINE' or part.ren_as == 'BILLBOARD':
749             row = layout.row()
750             col = row.column()
751             col.prop(part, "trail_count")
752             if part.trail_count > 1:
753                 col.prop(part, "abs_path_time", text="Length in frames")
754                 col = row.column()
755                 col.prop(part, "path_end", text="Length", slider=not part.abs_path_time)
756                 col.prop(part, "random_length", text="Random", slider=True)
757             else:
758                 col = row.column()
759                 col.label(text="")
760
761
762 class PARTICLE_PT_draw(ParticleButtonsPanel):
763     bl_label = "Display"
764     bl_default_closed = True
765
766     def poll(self, context):
767         psys = context.particle_system
768         if psys is None:
769             return False
770         if psys.settings is None:
771             return False
772         return True
773
774     def draw(self, context):
775         layout = self.layout
776
777         psys = context.particle_system
778         part = psys.settings
779
780         row = layout.row()
781         row.prop(part, "draw_as", expand=True)
782
783         if part.draw_as == 'NONE' or (part.ren_as == 'NONE' and part.draw_as == 'RENDER'):
784             return
785
786         path = (part.ren_as == 'PATH' and part.draw_as == 'RENDER') or part.draw_as == 'PATH'
787
788         if path and part.type != 'HAIR' and part.physics_type != 'KEYED' and psys.point_cache.baked is False:
789             box = layout.box()
790             box.label(text="Baked or keyed particles needed for correct drawing.")
791             return
792
793         row = layout.row()
794         row.prop(part, "display", slider=True)
795         if part.draw_as != 'RENDER' or part.ren_as == 'HALO':
796             row.prop(part, "draw_size")
797         else:
798             row.label(text="")
799
800         row = layout.row()
801         col = row.column()
802         col.prop(part, "show_size")
803         col.prop(part, "velocity")
804         col.prop(part, "num")
805         if part.physics_type == 'BOIDS':
806             col.prop(part, "draw_health")
807
808         col = row.column()
809         col.prop(part, "material_color", text="Use material color")
810
811         if (path):
812             col.prop(part, "draw_step")
813         else:
814             sub = col.column()
815             sub.active = (part.material_color is False)
816             #sub.label(text="color")
817             #sub.label(text="Override material color")
818
819
820 class PARTICLE_PT_children(ParticleButtonsPanel):
821     bl_label = "Children"
822     bl_default_closed = True
823
824     def draw(self, context):
825         layout = self.layout
826
827         psys = context.particle_system
828         part = psys.settings
829
830         layout.row().prop(part, "child_type", expand=True)
831
832         if part.child_type == 'NONE':
833             return
834
835         row = layout.row()
836
837         col = row.column(align=True)
838         col.prop(part, "child_nbr", text="Display")
839         col.prop(part, "rendered_child_nbr", text="Render")
840
841         col = row.column(align=True)
842
843         if part.child_type == 'FACES':
844             col.prop(part, "virtual_parents", slider=True)
845         else:
846             col.prop(part, "child_radius", text="Radius")
847             col.prop(part, "child_roundness", text="Roundness", slider=True)
848
849             col = row.column(align=True)
850             col.prop(part, "child_size", text="Size")
851             col.prop(part, "child_random_size", text="Random")
852
853         layout.row().label(text="Effects:")
854
855         row = layout.row()
856
857         col = row.column(align=True)
858         col.prop(part, "clump_factor", slider=True)
859         col.prop(part, "clumppow", slider=True)
860
861         col = row.column(align=True)
862         col.prop(part, "rough_endpoint")
863         col.prop(part, "rough_end_shape")
864
865         row = layout.row()
866
867         col = row.column(align=True)
868         col.prop(part, "rough1")
869         col.prop(part, "rough1_size")
870
871         col = row.column(align=True)
872         col.prop(part, "rough2")
873         col.prop(part, "rough2_size")
874         col.prop(part, "rough2_thres", slider=True)
875
876         row = layout.row()
877         col = row.column(align=True)
878         col.prop(part, "child_length", slider=True)
879         col.prop(part, "child_length_thres", slider=True)
880
881         col = row.column(align=True)
882         col.label(text="Space reserved for")
883         col.label(text="hair parting controls")
884
885         layout.row().label(text="Kink:")
886         layout.row().prop(part, "kink", expand=True)
887
888         split = layout.split()
889
890         col = split.column()
891         col.prop(part, "kink_amplitude")
892         col.prop(part, "kink_frequency")
893         col = split.column()
894         col.prop(part, "kink_shape", slider=True)
895
896
897 class PARTICLE_PT_field_weights(ParticleButtonsPanel):
898     bl_label = "Field Weights"
899     bl_default_closed = True
900
901     def draw(self, context):
902         part = context.particle_system.settings
903         effector_weights_ui(self, context, part.effector_weights)
904
905         if part.type == 'HAIR':
906             self.layout.prop(part.effector_weights, "do_growing_hair")
907
908
909 class PARTICLE_PT_force_fields(ParticleButtonsPanel):
910     bl_label = "Force Field Settings"
911     bl_default_closed = True
912
913     def draw(self, context):
914         layout = self.layout
915
916         part = context.particle_system.settings
917
918         layout.prop(part, "self_effect")
919
920         split = layout.split(percentage=0.2)
921         split.label(text="Type 1:")
922         split.prop(part.force_field_1, "type", text="")
923         basic_force_field_settings_ui(self, context, part.force_field_1)
924         basic_force_field_falloff_ui(self, context, part.force_field_1)
925
926         if part.force_field_1.type != 'NONE':
927             layout.label(text="")
928
929         split = layout.split(percentage=0.2)
930         split.label(text="Type 2:")
931         split.prop(part.force_field_2, "type", text="")
932         basic_force_field_settings_ui(self, context, part.force_field_2)
933         basic_force_field_falloff_ui(self, context, part.force_field_2)
934
935
936 class PARTICLE_PT_vertexgroups(ParticleButtonsPanel):
937     bl_label = "Vertexgroups"
938     bl_default_closed = True
939
940     def draw(self, context):
941         layout = self.layout
942
943         ob = context.object
944         psys = context.particle_system
945         # part = psys.settings
946
947         # layout.label(text="Nothing here yet.")
948
949         row = layout.row()
950         row.label(text="Vertex Group")
951         row.label(text="Negate")
952
953
954         row = layout.row()
955         row.prop_object(psys, "vertex_group_density", ob, "vertex_groups", text="Density")
956         row.prop(psys, "vertex_group_density_negate", text="")
957
958         row = layout.row()
959         row.prop_object(psys, "vertex_group_velocity", ob, "vertex_groups", text="Velocity")
960         row.prop(psys, "vertex_group_velocity_negate", text="")
961
962         row = layout.row()
963         row.prop_object(psys, "vertex_group_length", ob, "vertex_groups", text="Length")
964         row.prop(psys, "vertex_group_length_negate", text="")
965
966         row = layout.row()
967         row.prop_object(psys, "vertex_group_clump", ob, "vertex_groups", text="Clump")
968         row.prop(psys, "vertex_group_clump_negate", text="")
969
970         row = layout.row()
971         row.prop_object(psys, "vertex_group_kink", ob, "vertex_groups", text="Kink")
972         row.prop(psys, "vertex_group_kink_negate", text="")
973
974         row = layout.row()
975         row.prop_object(psys, "vertex_group_roughness1", ob, "vertex_groups", text="Roughness 1")
976         row.prop(psys, "vertex_group_roughness1_negate", text="")
977
978         row = layout.row()
979         row.prop_object(psys, "vertex_group_roughness2", ob, "vertex_groups", text="Roughness 2")
980         row.prop(psys, "vertex_group_roughness2_negate", text="")
981
982         row = layout.row()
983         row.prop_object(psys, "vertex_group_roughness_end", ob, "vertex_groups", text="Roughness End")
984         row.prop(psys, "vertex_group_roughness_end_negate", text="")
985
986         row = layout.row()
987         row.prop_object(psys, "vertex_group_size", ob, "vertex_groups", text="Size")
988         row.prop(psys, "vertex_group_size_negate", text="")
989
990         row = layout.row()
991         row.prop_object(psys, "vertex_group_tangent", ob, "vertex_groups", text="Tangent")
992         row.prop(psys, "vertex_group_tangent_negate", text="")
993
994         row = layout.row()
995         row.prop_object(psys, "vertex_group_rotation", ob, "vertex_groups", text="Rotation")
996         row.prop(psys, "vertex_group_rotation_negate", text="")
997
998         row = layout.row()
999         row.prop_object(psys, "vertex_group_field", ob, "vertex_groups", text="Field")
1000         row.prop(psys, "vertex_group_field_negate", text="")
1001
1002
1003 classes = [
1004     PARTICLE_PT_context_particles,
1005     PARTICLE_PT_hair_dynamics,
1006     PARTICLE_PT_cache,
1007     PARTICLE_PT_emission,
1008     PARTICLE_PT_velocity,
1009     PARTICLE_PT_rotation,
1010     PARTICLE_PT_physics,
1011     PARTICLE_PT_boidbrain,
1012     PARTICLE_PT_render,
1013     PARTICLE_PT_draw,
1014     PARTICLE_PT_children,
1015     PARTICLE_PT_field_weights,
1016     PARTICLE_PT_force_fields,
1017     PARTICLE_PT_vertexgroups,
1018
1019     PARTICLE_PT_custom_props]
1020
1021
1022 def register():
1023     register = bpy.types.register
1024     for cls in classes:
1025         register(cls)
1026
1027
1028 def unregister():
1029     unregister = bpy.types.unregister
1030     for cls in classes:
1031         unregister(cls)
1032
1033 if __name__ == "__main__":
1034     register()