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