Particle system parenting
[blender-staging.git] / release / ui / buttons_particle.py
1
2 import bpy
3
4 def particle_panel_enabled(psys):
5         return psys.point_cache.baked==False and psys.editable==False
6         
7 def particle_panel_poll(context):
8         psys = context.particle_system
9         if psys==None:  return False
10         return psys.settings.type in ('EMITTER', 'REACTOR', 'HAIR')
11
12 class ParticleButtonsPanel(bpy.types.Panel):
13         __space_type__ = "BUTTONS_WINDOW"
14         __region_type__ = "WINDOW"
15         __context__ = "particle"
16
17         def poll(self, context):
18                 return particle_panel_poll(context)
19
20 class PARTICLE_PT_particles(ParticleButtonsPanel):
21         __idname__= "PARTICLE_PT_particles"
22         __label__ = "Particle System"
23
24         def poll(self, context):
25                 return (context.particle_system or context.object)
26
27         def draw(self, context):
28                 layout = self.layout
29                 ob = context.object
30                 psys = context.particle_system
31
32                 split = layout.split(percentage=0.65)
33
34                 if psys:
35                         split.template_ID(context, psys, "settings")
36
37                 if psys:
38                         #row = layout.row()
39                         #row.itemL(text="Viewport")
40                         #row.itemL(text="Render")
41                         
42                         part = psys.settings
43                         ptype = psys.settings.type
44                         
45                         if ptype not in ('EMITTER', 'REACTOR', 'HAIR'):
46                                 layout.itemL(text="No settings for fluid particles")
47                                 return
48                                 
49                         split = layout.split(percentage=0.65)
50                         
51                         split.enabled = particle_panel_enabled(psys)
52                         split.itemR(part, "type")
53                         split.itemR(psys, "seed")
54                         
55                         split = layout.split(percentage=0.65)
56                         if part.type=='HAIR':
57                                 if psys.editable==True:
58                                         split.itemO("PARTICLE_OT_editable_set", text="Free Edit")
59                                 else:
60                                         split.itemO("PARTICLE_OT_editable_set", text="Make Editable")
61                                 row = split.row()
62                                 row.enabled = particle_panel_enabled(psys)
63                                 row.itemR(part, "hair_step")
64                         elif part.type=='REACTOR':
65                                 split.enabled = particle_panel_enabled(psys)
66                                 split.itemR(psys, "reactor_target_object")
67                                 split.itemR(psys, "reactor_target_particle_system", text="Particle System")
68                 
69 class PARTICLE_PT_emission(ParticleButtonsPanel):
70         __idname__= "PARTICLE_PT_emission"
71         __label__ = "Emission"
72         
73         def draw(self, context):
74                 layout = self.layout
75
76                 psys = context.particle_system
77                 part = psys.settings
78                 
79                 layout.enabled = particle_panel_enabled(psys)
80                 
81                 row = layout.row()
82                 row.itemR(part, "amount")
83                 
84                 split = layout.split()
85                 
86                 col = split.column(align=True)
87                 col.itemR(part, "start")
88                 col.itemR(part, "end")
89
90                 col = split.column(align=True)
91                 col.itemR(part, "lifetime")
92                 col.itemR(part, "random_lifetime", slider=True)
93                 
94                 layout.row().itemL(text="Emit From:")
95                 
96                 row = layout.row()
97                 row.itemR(part, "emit_from", expand=True)
98                 row = layout.row()
99                 row.itemR(part, "trand")
100                 if part.distribution!='GRID':
101                         row.itemR(part, "even_distribution")
102                 
103                 if part.emit_from=='FACE' or part.emit_from=='VOLUME':
104                         row = layout.row()
105                         row.itemR(part, "distribution", expand=True)
106                         
107                         row = layout.row()
108
109                         if part.distribution=='JIT':
110                                 row.itemR(part, "userjit", text="Particles/Face")
111                                 row.itemR(part, "jitter_factor", text="Jittering Amount", slider=True)
112                         elif part.distribution=='GRID':
113                                 row.itemR(part, "grid_resolution")
114
115 class PARTICLE_PT_cache(ParticleButtonsPanel):
116         __idname__= "PARTICLE_PT_cache"
117         __label__ = "Cache"
118         __default_closed__ = True
119         
120         def poll(self, context):
121                 psys = context.particle_system
122                 if psys==None:  return False
123                 return psys.settings.type in ('EMITTER', 'REACTOR')
124
125         def draw(self, context):
126                 layout = self.layout
127
128                 psys = context.particle_system
129                 part = psys.settings
130                 cache = psys.point_cache
131                 
132                 row = layout.row()
133                 row.itemR(cache, "name", text="")
134                 if cache.outdated:
135                         row.itemL(text="Cache is outdated.")
136                 else:
137                         row.itemL(text="")
138                 
139                 row = layout.row()
140                 
141                 if cache.baked == True:
142                         row.itemO("PTCACHE_OT_free_bake_particle_system", text="Free Bake")
143                 else:
144                         row.item_booleanO("PTCACHE_OT_cache_particle_system", "bake", True, text="Bake")
145                         
146                 row = layout.row()
147                 row.enabled = particle_panel_enabled(psys)
148                 row.itemO("PTCACHE_OT_bake_from_particles_cache", text="Current Cache to Bake")
149                 if cache.autocache == 0:
150                         row.itemO("PTCACHE_OT_cache_particle_system", text="Cache to Current Frame")
151         
152                 row = layout.row()
153                 row.enabled = particle_panel_enabled(psys)
154                 #row.itemR(cache, "autocache")
155                 row.itemR(cache, "disk_cache")
156                 row.itemL(text=cache.info)
157                 
158                 # for particles these are figured out automatically
159                 #row.itemR(cache, "start_frame")
160                 #row.itemR(cache, "end_frame")
161
162 class PARTICLE_PT_initial(ParticleButtonsPanel):
163         __idname__= "PARTICLE_PT_initial"
164         __label__ = "Velocity"
165
166         def draw(self, context):
167                 layout = self.layout
168
169                 psys = context.particle_system
170                 part = psys.settings
171                 
172                 layout.enabled = particle_panel_enabled(psys)
173                                 
174                 layout.row().itemL(text="Direction:")
175         
176                 split = layout.split()
177                         
178                 sub = split.column()
179                 sub.itemR(part, "normal_factor")
180                 if part.emit_from=='PARTICLE':
181                         sub.itemR(part, "particle_factor")
182                 else:
183                         sub.itemR(part, "object_factor", slider=True)
184                 sub.itemR(part, "random_factor")
185                 sub.itemR(part, "tangent_factor")
186                 sub.itemR(part, "tangent_phase", slider=True)
187                 
188                 sub = split.column()
189                 sub.itemL(text="TODO:")
190                 sub.itemL(text="Object aligned")
191                 sub.itemL(text="direction: X, Y, Z")
192                 
193                 if part.type=='REACTOR':
194                         sub.itemR(part, "reactor_factor")
195                         sub.itemR(part, "reaction_shape", slider=True)
196                 else:
197                         sub.itemL(text="")
198                 
199                 layout.row().itemL(text="Rotation:")
200                 split = layout.split()
201                         
202                 sub = split.column()
203                 
204                 sub.itemR(part, "rotation_mode", text="Axis")
205                 split = layout.split()
206                         
207                 sub = split.column()
208                 sub.itemR(part, "rotation_dynamic")
209                 sub.itemR(part, "random_rotation_factor", slider=True)
210                 sub = split.column()
211                 sub.itemR(part, "phase_factor", slider=True)
212                 sub.itemR(part, "random_phase_factor", text="Random", slider=True)
213
214                 layout.row().itemL(text="Angular velocity:")
215                 layout.row().itemR(part, "angular_velocity_mode", expand=True)
216                 split = layout.split()
217                         
218                 sub = split.column()
219                 
220                 sub.itemR(part, "angular_velocity_factor", text="")
221                 
222 class PARTICLE_PT_physics(ParticleButtonsPanel):
223         __idname__= "PARTICLE_PT_physics"
224         __label__ = "Physics"
225
226         def draw(self, context):
227                 layout = self.layout
228
229                 psys = context.particle_system
230                 part = psys.settings
231                 
232                 layout.enabled = layout.enabled = particle_panel_enabled(psys)
233
234                 row = layout.row()
235                 row.itemR(part, "physics_type", expand=True)
236                 if part.physics_type != 'NO':
237                         layout.itemR(part, "effector_group")
238                 
239                         row = layout.row()
240                         col = row.column(align=True)
241                         col.itemR(part, "particle_size")
242                         col.itemR(part, "random_size", slider=True)
243                         col = row.column(align=True)
244                         col.itemR(part, "mass")
245                         col.itemR(part, "sizemass", text="Multiply mass with size")
246                                                         
247                         split = layout.split()
248                         
249                         sub = split.column()
250                         
251                 if part.physics_type == 'NEWTON':
252                         
253                         sub.itemL(text="Forces:")
254                         sub.itemR(part, "brownian_factor")
255                         sub.itemR(part, "drag_factor", slider=True)
256                         sub.itemR(part, "damp_factor", slider=True)
257                         sub.itemR(part, "integrator")
258                         sub = split.column()
259                         sub.itemR(part, "acceleration")
260                         
261                 elif part.physics_type == 'KEYED':
262                         sub.itemR(psys, "keyed_first")
263                         if psys.keyed_first==True:
264                                 sub.itemR(psys, "timed_keys", text="Key timing")
265                         else:
266                                 sub.itemR(part, "keyed_time")
267                         sub = split.column()
268                         sub.itemL(text="Next key from object:")
269                         sub.itemR(psys, "keyed_object", text="")
270                         sub.itemR(psys, "keyed_particle_system")
271                 
272                 if part.physics_type=='NEWTON' or part.physics_type=='BOIDS':
273
274                         sub.itemR(part, "size_deflect")
275                         sub.itemR(part, "die_on_collision")
276                         sub.itemR(part, "sticky")
277
278 class PARTICLE_PT_render(ParticleButtonsPanel):
279         __idname__= "PARTICLE_PT_render"
280         __label__ = "Render"
281         
282         def poll(self, context):
283                 return (context.particle_system != None)
284                 
285         def draw(self, context):
286                 layout = self.layout
287
288                 psys = context.particle_system
289                 part = psys.settings
290                 
291                 row = layout.row()
292                 row.itemR(part, "material")
293                 row.itemR(psys, "parent");
294                 
295                 split = layout.split()
296                         
297                 sub = split.column()
298                 sub.itemR(part, "emitter");
299                 sub.itemR(part, "parent");
300                 sub = split.column()
301                 sub.itemR(part, "unborn");
302                 sub.itemR(part, "died");
303                 
304                 row = layout.row()
305                 row.itemR(part, "ren_as", expand=True)
306                 
307                 split = layout.split()
308                         
309                 sub = split.column()
310                 
311                 if part.ren_as == 'LINE':
312                         sub.itemR(part, "line_length_tail")
313                         sub.itemR(part, "line_length_head")
314                         sub = split.column()
315                         sub.itemR(part, "velocity_length")
316                 elif part.ren_as == 'PATH':
317                 
318                         if (part.type!='HAIR' and psys.point_cache.baked==False):
319                                 box = layout.box()
320                                 box.itemL(text="Baked or keyed particles needed for correct rendering.")
321                                 return
322                                 
323                         sub.itemR(part, "render_strand")
324                         colsub = sub.column()
325                         colsub.active = part.render_strand == False
326                         colsub.itemR(part, "render_adaptive")
327                         colsub = sub.column()
328                         colsub.active = part.render_adaptive or part.render_strand == True
329                         colsub.itemR(part, "adaptive_angle")
330                         colsub = sub.column()
331                         colsub.active = part.render_adaptive == True and part.render_strand == False
332                         colsub.itemR(part, "adaptive_pix")
333                         sub.itemR(part, "hair_bspline")
334                         sub.itemR(part, "render_step", text="Steps")
335                         sub = split.column()
336                         sub.itemL(text="Length:")
337                         sub.itemR(part, "abs_length", text="Absolute")
338                         sub.itemR(part, "absolute_length", text="Maximum")
339                         sub.itemR(part, "random_length", text="Random", slider=True)
340                         
341                         #row = layout.row()
342                         #row.itemR(part, "timed_path")
343                         #col = row.column(align=True)
344                         #col.active = part.timed_path == True
345                         #col.itemR(part, "line_length_tail", text="Start")
346                         #col.itemR(part, "line_length_head", text="End")
347                         
348                         row = layout.row()
349                         col = row.column()
350                         
351                         if part.type=='HAIR' and part.render_strand==True and part.child_type=='FACES':
352                                 layout.itemR(part, "enable_simplify")
353                                 if part.enable_simplify==True:
354                                         row = layout.row()
355                                         row.itemR(part, "simplify_refsize")
356                                         row.itemR(part, "simplify_rate")
357                                         row.itemR(part, "simplify_transition")
358                                         row = layout.row()
359                                         row.itemR(part, "viewport")
360                                         subrow = row.row()
361                                         subrow.active = part.viewport==True
362                                         subrow.itemR(part, "simplify_viewport")
363                         
364
365                 elif part.ren_as == 'OBJECT':
366                         #sub = split.column()
367                         sub.itemR(part, "dupli_object")
368                 elif part.ren_as == 'GROUP':
369                         sub.itemR(part, "dupli_group")
370                         split = layout.split()
371                         sub = split.column()
372                         sub.itemR(part, "whole_group")
373                         sub = split.column()
374                         colsub = sub.column()
375                         colsub.active = part.whole_group == False
376                         colsub.itemR(part, "rand_group")
377                         
378                 elif part.ren_as == 'BILLBOARD':
379                         sub.itemL(text="Align:")
380                         
381                         row = layout.row()
382                         row.itemR(part, "billboard_align", expand=True)
383                         row.itemR(part, "billboard_lock", text="Lock")
384                         row = layout.row()
385                         row.itemR(part, "billboard_object")
386                 
387                         row = layout.row()
388                         col = row.column(align=True)
389                         col.itemL(text="Tilt:")
390                         col.itemR(part, "billboard_tilt", text="Angle", slider=True)
391                         col.itemR(part, "billboard_random_tilt", slider=True)
392                         col = row.column()
393                         col.itemR(part, "billboard_offset")
394                         
395                         row = layout.row()
396                         row.itemR(psys, "billboard_normal_uv")
397                         row = layout.row()
398                         row.itemR(psys, "billboard_time_index_uv")
399                         
400                         row = layout.row()
401                         row.itemL(text="Split uv's:")
402                         row.itemR(part, "billboard_uv_split", text="Number of splits")
403                         row = layout.row()
404                         row.itemR(psys, "billboard_split_uv")
405                         row = layout.row()
406                         row.itemL(text="Animate:")
407                         row.itemR(part, "billboard_animation", expand=True)
408                         row.itemL(text="Offset:")
409                         row.itemR(part, "billboard_split_offset", expand=True)
410                 
411 class PARTICLE_PT_draw(ParticleButtonsPanel):
412         __idname__= "PARTICLE_PT_draw"
413         __label__ = "Display"
414         __default_closed__ = True
415         
416         def poll(self, context):
417                 return (context.particle_system != None)
418         
419         def draw(self, context):
420                 layout = self.layout
421
422                 psys = context.particle_system
423                 part = psys.settings
424                 
425                 row = layout.row()
426                 row.itemR(part, "draw_as", expand=True)
427                 
428                 if part.draw_as=='NONE' or (part.ren_as=='NONE' and part.draw_as=='RENDER'):
429                         return
430                         
431                 path = (part.ren_as=='PATH' and part.draw_as=='RENDER') or part.draw_as=='PATH'
432                         
433                 if path and part.type!='HAIR' and psys.point_cache.baked==False:
434                         box = layout.box()
435                         box.itemL(text="Baked or keyed particles needed for correct drawing.")
436                         return
437                 
438                 row = layout.row()
439                 row.itemR(part, "display", slider=True)
440                 if part.draw_as!='RENDER' or part.ren_as=='HALO':
441                         row.itemR(part, "draw_size")
442                 else:
443                         row.itemL(text="")
444                 
445                 row = layout.row()
446                 col = row.column()
447                 col.itemR(part, "show_size")
448                 col.itemR(part, "velocity")
449                 col.itemR(part, "num")
450                 if part.physics_type == 'BOIDS':
451                         col.itemR(part, "draw_health")
452                 
453                 col = row.column()
454                 if (path):
455                         box = col.box()                         
456                         box.itemR(part, "draw_step")
457                 else:
458                         col.itemR(part, "material_color", text="Use material color")
459                         subcol = col.column()
460                         subcol.active = part.material_color==False
461                         #subcol.itemL(text="color")
462                         #subcol.itemL(text="Override material color")
463
464 class PARTICLE_PT_children(ParticleButtonsPanel):
465         __idname__= "PARTICLE_PT_children"
466         __label__ = "Children"
467         __default_closed__ = True
468
469         def draw(self, context):
470                 layout = self.layout
471
472                 psys = context.particle_system
473                 part = psys.settings
474                 
475                 layout.row().itemR(part, "child_type", expand=True)
476                 
477                 if part.child_type=='NONE':
478                         return
479                 
480                 row = layout.row()
481                 
482                 col = row.column(align=True)
483                 col.itemR(part, "child_nbr", text="Display")
484                 col.itemR(part, "rendered_child_nbr", text="Render")
485                 
486                 col = row.column(align=True)
487                 
488                 if part.child_type=='FACES':
489                         col.itemR(part, "virtual_parents", slider=True)
490                 else:
491                         col.itemR(part, "child_radius", text="Radius")
492                         col.itemR(part, "child_roundness", text="Roundness", slider=True)
493                 
494                         col = row.column(align=True)
495                         col.itemR(part, "child_size", text="Size")
496                         col.itemR(part, "child_random_size", text="Random")
497                 
498                 layout.row().itemL(text="Effects:")
499                 
500                 row = layout.row()
501                 
502                 col = row.column(align=True)
503                 col.itemR(part, "clump_factor", slider=True)
504                 col.itemR(part, "clumppow", slider=True)
505                 
506                 col = row.column(align=True)
507                 col.itemR(part, "rough_endpoint")
508                 col.itemR(part, "rough_end_shape")
509
510                 row = layout.row()
511                 
512                 col = row.column(align=True)
513                 col.itemR(part, "rough1")
514                 col.itemR(part, "rough1_size")
515
516                 col = row.column(align=True)
517                 col.itemR(part, "rough2")
518                 col.itemR(part, "rough2_size")
519                 col.itemR(part, "rough2_thres", slider=True)
520                 
521                 layout.row().itemL(text="Kink:")
522                 layout.row().itemR(part, "kink", expand=True)
523                 
524                 split = layout.split()
525                 
526                 sub = split.column()
527                 sub.itemR(part, "kink_amplitude")
528                 sub.itemR(part, "kink_frequency")
529                 sub = split.column()
530                 sub.itemR(part, "kink_shape", slider=True)
531
532 class PARTICLE_PT_vertexgroups(ParticleButtonsPanel):
533         __idname__= "PARTICLE_PT_vertexgroups"
534         __label__ = "Vertexgroups"
535         __default_closed__ = True
536
537         def draw(self, context):
538                 layout = self.layout
539
540                 psys = context.particle_system
541                 part = psys.settings
542                 
543                 layout.itemL(text="Nothing here yet.")
544
545                 #row = layout.row()
546                 #row.itemL(text="Vertex Group")
547                 #row.itemL(text="Negate")
548
549                 
550                 #row = layout.row()
551                 #row.itemR(psys, "vertex_group_density")
552                 #row.itemR(psys, "vertex_group_density_negate", text="")
553                 
554                 #row = layout.row()
555                 #row.itemR(psys, "vertex_group_velocity")
556                 #row.itemR(psys, "vertex_group_velocity_negate", text="")
557                 
558                 #row = layout.row()
559                 #row.itemR(psys, "vertex_group_length")
560                 #row.itemR(psys, "vertex_group_length_negate", text="")
561                 
562                 #row = layout.row()
563                 #row.itemR(psys, "vertex_group_clump")
564                 #row.itemR(psys, "vertex_group_clump_negate", text="")
565                 
566                 #row = layout.row()
567                 #row.itemR(psys, "vertex_group_kink")
568                 #row.itemR(psys, "vertex_group_kink_negate", text="")
569                 
570                 #row = layout.row()
571                 #row.itemR(psys, "vertex_group_roughness1")
572                 #row.itemR(psys, "vertex_group_roughness1_negate", text="")
573                 
574                 #row = layout.row()
575                 #row.itemR(psys, "vertex_group_roughness2")
576                 #row.itemR(psys, "vertex_group_roughness2_negate", text="")
577                 
578                 #row = layout.row()
579                 #row.itemR(psys, "vertex_group_roughness_end")
580                 #row.itemR(psys, "vertex_group_roughness_end_negate", text="")
581
582                 #row = layout.row()
583                 #row.itemR(psys, "vertex_group_size")
584                 #row.itemR(psys, "vertex_group_size_negate", text="")
585                 
586                 #row = layout.row()
587                 #row.itemR(psys, "vertex_group_tangent")
588                 #row.itemR(psys, "vertex_group_tangent_negate", text="")
589                 
590                 #row = layout.row()
591                 #row.itemR(psys, "vertex_group_rotation")
592                 #row.itemR(psys, "vertex_group_rotation_negate", text="")
593                 
594                 #row = layout.row()
595                 #row.itemR(psys, "vertex_group_field")
596                 #row.itemR(psys, "vertex_group_field_negate", text="")
597                 
598 bpy.types.register(PARTICLE_PT_particles)
599 bpy.types.register(PARTICLE_PT_cache)
600 bpy.types.register(PARTICLE_PT_emission)
601 bpy.types.register(PARTICLE_PT_initial)
602 bpy.types.register(PARTICLE_PT_physics)
603 bpy.types.register(PARTICLE_PT_render)
604 bpy.types.register(PARTICLE_PT_draw)
605 bpy.types.register(PARTICLE_PT_children)
606 bpy.types.register(PARTICLE_PT_vertexgroups)