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