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