forked from bartvdbraak/blender
17a9b3e44c
Particle point cache can now be loaded from external files. - Activated by "external" checkbox in cache panel and giving proper folder path and file name identifier. - External cache panel has controls for particle emission start, end, lifetime and random lifetime. These should be set according to the external data for correct playback. - External files should be named "identifier_frame_index.bphys" or "identifier_frame.bphys" where: * "identifier" is a freely choseable name. * "frame" is the cached frame number. ** Six digits padded with zeros!, for example "000024". * "index" can be used to tell caches with the same identifier apart. ** Two digits starting from zero. ** The index and the underscore before are optional. If no index is present the index number in ui should be set to -1. - Cache file format is pure floating point numbers (in binary, not text!) with each particle's data one after the other with the following data members: * 3 floats: particle's location vector * 3 floats: particle's velocity vector (per second) * 4 floats: particle's rotation quaternion * 3 floats: particle's angular velocity vector (per second) * 1 float: frame of the actual data (this can be non-integer for particles that are born or die between two integer frames, but otherwise should be the same as the "frame" in the file name) - Cache files don't have to exist for each frame. * Frames without actual data are interpolated from surrounding frames that have data (extrapolation is not supported). - Cache file formats with extended (or reduced even) data members are in future plans for easier usage. - Current code only does particles, don't yet know if it's applicable to cloth or sb. - Known issue: endianness can't yet be handled in any way. Other changes: New hard limits for many particle parameters. Some examples: - Maximum amount of particles: 10M particles :) And before you all go and crash your Blender trying this out remember that this limit is only for those freaks who really have the machine power to handle it. 10M particles alone take around 2.2 Gb of memory / disk space in saved file and each cached frame takes around 0.5 Gb of memory / disk space depending on cache mode. * Known issue: To actually use this many particles they most likely need to be allocated in parts as taking hold of a 2.2Gb chunk of memory at once is probably not ok with any operating system. - Maximum amount of children: 100k children/particle (1T childparticles here we come :D) - Kink frequency: -100k to 100k half-rotations (really strange the previous limit was only from zero upwards) - Path draw steps: 10 (power of 2 remember) - Path render steps: 20 (power of 2 also!! If over 1M segments doesn't get you smooth paths then I think nothing will!)
903 lines
26 KiB
Python
903 lines
26 KiB
Python
|
|
import bpy
|
|
|
|
def particle_panel_enabled(psys):
|
|
return psys.point_cache.baked==False and psys.editable==False
|
|
|
|
def particle_panel_poll(context):
|
|
psys = context.particle_system
|
|
if psys==None: return False
|
|
if psys.settings==None: return False
|
|
return psys.settings.type in ('EMITTER', 'REACTOR', 'HAIR')
|
|
|
|
class ParticleButtonsPanel(bpy.types.Panel):
|
|
__space_type__ = "BUTTONS_WINDOW"
|
|
__region_type__ = "WINDOW"
|
|
__context__ = "particle"
|
|
|
|
def poll(self, context):
|
|
return particle_panel_poll(context)
|
|
|
|
class PARTICLE_PT_particles(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_particles"
|
|
__no_header__ = True
|
|
|
|
def poll(self, context):
|
|
return (context.particle_system or context.object)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob = context.object
|
|
psys = context.particle_system
|
|
|
|
if ob:
|
|
row = layout.row()
|
|
|
|
row.template_list(ob, "particle_systems", ob, "active_particle_system_index")
|
|
|
|
col = row.column(align=True)
|
|
col.itemO("object.particle_system_add", icon="ICON_ZOOMIN", text="")
|
|
col.itemO("object.particle_system_remove", icon="ICON_ZOOMOUT", text="")
|
|
|
|
if psys:
|
|
part = psys.settings
|
|
|
|
split = layout.split(percentage=0.32)
|
|
col = split.column()
|
|
col.itemL(text="Name:")
|
|
if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
|
|
col.itemL(text="Settings:")
|
|
col.itemL(text="Type:")
|
|
|
|
col = split.column()
|
|
col.itemR(psys, "name", text="")
|
|
if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
|
|
col.template_ID(psys, "settings", new="particle.new")
|
|
|
|
#row = layout.row()
|
|
#row.itemL(text="Viewport")
|
|
#row.itemL(text="Render")
|
|
|
|
if part:
|
|
if part.type not in ('EMITTER', 'REACTOR', 'HAIR'):
|
|
layout.itemL(text="No settings for fluid particles")
|
|
return
|
|
|
|
row=col.row()
|
|
row.enabled = particle_panel_enabled(psys)
|
|
row.itemR(part, "type", text="")
|
|
row.itemR(psys, "seed")
|
|
|
|
split = layout.split(percentage=0.65)
|
|
if part.type=='HAIR':
|
|
if psys.editable==True:
|
|
split.itemO("particle.editable_set", text="Free Edit")
|
|
else:
|
|
split.itemO("particle.editable_set", text="Make Editable")
|
|
row = split.row()
|
|
row.enabled = particle_panel_enabled(psys)
|
|
row.itemR(part, "hair_step")
|
|
elif part.type=='REACTOR':
|
|
split.enabled = particle_panel_enabled(psys)
|
|
split.itemR(psys, "reactor_target_object")
|
|
split.itemR(psys, "reactor_target_particle_system", text="Particle System")
|
|
|
|
class PARTICLE_PT_emission(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_emission"
|
|
__label__ = "Emission"
|
|
|
|
def poll(self, context):
|
|
if particle_panel_poll(context):
|
|
return not context.particle_system.point_cache.external
|
|
else:
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.enabled = particle_panel_enabled(psys)
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "amount")
|
|
|
|
split = layout.split()
|
|
|
|
col = split.column(align=True)
|
|
col.itemR(part, "start")
|
|
col.itemR(part, "end")
|
|
|
|
col = split.column(align=True)
|
|
col.itemR(part, "lifetime")
|
|
col.itemR(part, "random_lifetime", slider=True)
|
|
|
|
layout.row().itemL(text="Emit From:")
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "emit_from", expand=True)
|
|
row = layout.row()
|
|
row.itemR(part, "trand")
|
|
if part.distribution!='GRID':
|
|
row.itemR(part, "even_distribution")
|
|
|
|
if part.emit_from=='FACE' or part.emit_from=='VOLUME':
|
|
row = layout.row()
|
|
row.itemR(part, "distribution", expand=True)
|
|
|
|
row = layout.row()
|
|
|
|
if part.distribution=='JIT':
|
|
row.itemR(part, "userjit", text="Particles/Face")
|
|
row.itemR(part, "jitter_factor", text="Jittering Amount", slider=True)
|
|
elif part.distribution=='GRID':
|
|
row.itemR(part, "grid_resolution")
|
|
|
|
class PARTICLE_PT_cache(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_cache"
|
|
__label__ = "Cache"
|
|
__default_closed__ = True
|
|
|
|
def poll(self, context):
|
|
psys = context.particle_system
|
|
if psys==None: return False
|
|
if psys.settings==None: return False
|
|
phystype = psys.settings.physics_type
|
|
if phystype == 'NO' or phystype == 'KEYED':
|
|
return False
|
|
return psys.settings.type in ('EMITTER', 'REACTOR')
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
cache = psys.point_cache
|
|
|
|
row = layout.row()
|
|
row.itemL(text="File Name:")
|
|
row.itemR(cache, "external")
|
|
|
|
if cache.external:
|
|
split = layout.split(percentage=0.80)
|
|
split.itemR(cache, "name", text="")
|
|
split.itemR(cache, "index", text="")
|
|
|
|
layout.itemL(text="File Path:")
|
|
layout.itemR(cache, "filepath", text="")
|
|
|
|
layout.itemL(text=cache.info)
|
|
|
|
split = layout.split()
|
|
|
|
col = split.column(align=True)
|
|
col.itemR(part, "start")
|
|
col.itemR(part, "end")
|
|
|
|
col = split.column(align=True)
|
|
col.itemR(part, "lifetime")
|
|
col.itemR(part, "random_lifetime", slider=True)
|
|
else:
|
|
layout.itemR(cache, "name", text="")
|
|
|
|
row = layout.row()
|
|
|
|
if cache.baked == True:
|
|
row.itemO("ptcache.free_bake_particle_system", text="Free Bake")
|
|
else:
|
|
row.item_booleanO("ptcache.cache_particle_system", "bake", True, text="Bake")
|
|
|
|
subrow = row.row()
|
|
subrow.enabled = (cache.frames_skipped or cache.outdated) and particle_panel_enabled(psys)
|
|
subrow.itemO("ptcache.cache_particle_system", text="Calculate to Current Frame")
|
|
|
|
row = layout.row()
|
|
row.enabled = particle_panel_enabled(psys)
|
|
row.itemO("ptcache.bake_from_particles_cache", text="Current Cache to Bake")
|
|
row.itemR(cache, "step");
|
|
|
|
row = layout.row()
|
|
row.enabled = particle_panel_enabled(psys)
|
|
row.itemR(cache, "quick_cache")
|
|
row.itemR(cache, "disk_cache")
|
|
|
|
layout.itemL(text=cache.info)
|
|
|
|
layout.itemS()
|
|
|
|
row = layout.row()
|
|
row.item_booleanO("ptcache.bake_all", "bake", True, text="Bake All Dynamics")
|
|
row.itemO("ptcache.free_bake_all", text="Free All Bakes")
|
|
layout.itemO("ptcache.bake_all", text="Update All Dynamics to current frame")
|
|
|
|
# for particles these are figured out automatically
|
|
#row.itemR(cache, "start_frame")
|
|
#row.itemR(cache, "end_frame")
|
|
|
|
class PARTICLE_PT_initial(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_initial"
|
|
__label__ = "Velocity"
|
|
|
|
def poll(self, context):
|
|
if particle_panel_poll(context):
|
|
psys = context.particle_system
|
|
return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.external
|
|
else:
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.enabled = particle_panel_enabled(psys)
|
|
|
|
layout.row().itemL(text="Direction:")
|
|
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
sub.itemR(part, "normal_factor")
|
|
if part.emit_from=='PARTICLE':
|
|
sub.itemR(part, "particle_factor")
|
|
else:
|
|
sub.itemR(part, "object_factor", slider=True)
|
|
sub.itemR(part, "random_factor")
|
|
sub.itemR(part, "tangent_factor")
|
|
sub.itemR(part, "tangent_phase", slider=True)
|
|
|
|
sub = split.column()
|
|
sub.itemL(text="TODO:")
|
|
sub.itemL(text="Object aligned")
|
|
sub.itemL(text="direction: X, Y, Z")
|
|
|
|
if part.type=='REACTOR':
|
|
sub.itemR(part, "reactor_factor")
|
|
sub.itemR(part, "reaction_shape", slider=True)
|
|
else:
|
|
sub.itemL(text="")
|
|
|
|
layout.row().itemL(text="Rotation:")
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
|
|
sub.itemR(part, "rotation_mode", text="Axis")
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
sub.itemR(part, "rotation_dynamic")
|
|
sub.itemR(part, "random_rotation_factor", slider=True)
|
|
sub = split.column()
|
|
sub.itemR(part, "phase_factor", slider=True)
|
|
sub.itemR(part, "random_phase_factor", text="Random", slider=True)
|
|
|
|
layout.row().itemL(text="Angular velocity:")
|
|
layout.row().itemR(part, "angular_velocity_mode", expand=True)
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
|
|
sub.itemR(part, "angular_velocity_factor", text="")
|
|
|
|
class PARTICLE_PT_physics(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_physics"
|
|
__label__ = "Physics"
|
|
|
|
def poll(self, context):
|
|
if particle_panel_poll(context):
|
|
return not context.particle_system.point_cache.external
|
|
else:
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.enabled = particle_panel_enabled(psys)
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "physics_type", expand=True)
|
|
if part.physics_type != 'NO':
|
|
row = layout.row()
|
|
col = row.column(align=True)
|
|
col.itemR(part, "particle_size")
|
|
col.itemR(part, "random_size", slider=True)
|
|
col = row.column(align=True)
|
|
col.itemR(part, "mass")
|
|
col.itemR(part, "sizemass", text="Multiply mass with size")
|
|
|
|
if part.physics_type == 'NEWTON':
|
|
split = layout.split()
|
|
sub = split.column()
|
|
|
|
sub.itemL(text="Forces:")
|
|
sub.itemR(part, "brownian_factor")
|
|
sub.itemR(part, "drag_factor", slider=True)
|
|
sub.itemR(part, "damp_factor", slider=True)
|
|
sub.itemR(part, "integrator")
|
|
sub = split.column()
|
|
sub.itemR(part, "acceleration")
|
|
|
|
elif part.physics_type == 'KEYED':
|
|
split = layout.split()
|
|
sub = split.column()
|
|
|
|
row = layout.row()
|
|
col = row.column()
|
|
col.active = not psys.keyed_timing
|
|
col.itemR(part, "keyed_loops", text="Loops")
|
|
row.itemR(psys, "keyed_timing", text="Use Timing")
|
|
|
|
layout.itemL(text="Keys:")
|
|
elif part.physics_type=='BOIDS':
|
|
boids = part.boids
|
|
|
|
|
|
row = layout.row()
|
|
row.itemR(boids, "allow_flight")
|
|
row.itemR(boids, "allow_land")
|
|
row.itemR(boids, "allow_climb")
|
|
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
col = sub.column(align=True)
|
|
col.active = boids.allow_flight
|
|
col.itemR(boids, "air_max_speed")
|
|
col.itemR(boids, "air_min_speed", slider="True")
|
|
col.itemR(boids, "air_max_acc", slider="True")
|
|
col.itemR(boids, "air_max_ave", slider="True")
|
|
col.itemR(boids, "air_personal_space")
|
|
row = col.row()
|
|
row.active = (boids.allow_land or boids.allow_climb) and boids.allow_flight
|
|
row.itemR(boids, "landing_smoothness")
|
|
|
|
sub = split.column()
|
|
col = sub.column(align=True)
|
|
col.active = boids.allow_land or boids.allow_climb
|
|
col.itemR(boids, "land_max_speed")
|
|
col.itemR(boids, "land_jump_speed")
|
|
col.itemR(boids, "land_max_acc", slider="True")
|
|
col.itemR(boids, "land_max_ave", slider="True")
|
|
col.itemR(boids, "land_personal_space")
|
|
col.itemR(boids, "land_stick_force")
|
|
|
|
row = layout.row()
|
|
|
|
col = row.column(align=True)
|
|
col.itemL(text="Battle:")
|
|
col.itemR(boids, "health")
|
|
col.itemR(boids, "strength")
|
|
col.itemR(boids, "aggression")
|
|
col.itemR(boids, "accuracy")
|
|
col.itemR(boids, "range")
|
|
|
|
col = row.column()
|
|
col.itemL(text="Misc:")
|
|
col.itemR(part, "gravity")
|
|
col.itemR(boids, "banking", slider=True)
|
|
col.itemR(boids, "height", slider=True)
|
|
|
|
if part.physics_type=='NEWTON':
|
|
sub.itemR(part, "size_deflect")
|
|
sub.itemR(part, "die_on_collision")
|
|
sub.itemR(part, "sticky")
|
|
elif part.physics_type=='KEYED' or part.physics_type=='BOIDS':
|
|
if part.physics_type=='BOIDS':
|
|
layout.itemL(text="Relations:")
|
|
|
|
row = layout.row()
|
|
row.template_list(psys, "targets", psys, "active_particle_target_index")
|
|
|
|
col = row.column()
|
|
subrow = col.row()
|
|
subcol = subrow.column(align=True)
|
|
subcol.itemO("particle.new_target", icon="ICON_ZOOMIN", text="")
|
|
subcol.itemO("particle.remove_target", icon="ICON_ZOOMOUT", text="")
|
|
subrow = col.row()
|
|
subcol = subrow.column(align=True)
|
|
subcol.itemO("particle.target_move_up", icon="VICON_MOVE_UP", text="")
|
|
subcol.itemO("particle.target_move_down", icon="VICON_MOVE_DOWN", text="")
|
|
|
|
key = psys.active_particle_target
|
|
if key:
|
|
row = layout.row()
|
|
if part.physics_type=='KEYED':
|
|
col = row.column()
|
|
#doesn't work yet
|
|
#col.red_alert = key.valid
|
|
col.itemR(key, "object", text="")
|
|
col.itemR(key, "system", text="System")
|
|
col = row.column();
|
|
col.active = psys.keyed_timing
|
|
col.itemR(key, "time")
|
|
col.itemR(key, "duration")
|
|
else:
|
|
subrow = row.row()
|
|
#doesn't work yet
|
|
#subrow.red_alert = key.valid
|
|
subrow.itemR(key, "object", text="")
|
|
subrow.itemR(key, "system", text="System")
|
|
|
|
layout.itemR(key, "mode", expand=True)
|
|
|
|
class PARTICLE_PT_boidbrain(ParticleButtonsPanel):
|
|
__idname__ = "PARTICLE_PT_boidbrain"
|
|
__label__ = "Boid Brain"
|
|
|
|
def poll(self, context):
|
|
psys = context.particle_system
|
|
if psys==None: return False
|
|
if psys.settings==None: return False
|
|
if psys.point_cache.external: return False
|
|
return psys.settings.physics_type=='BOIDS'
|
|
|
|
def draw(self, context):
|
|
boids = context.particle_system.settings.boids
|
|
layout = self.layout
|
|
|
|
layout.enabled = particle_panel_enabled(psys)
|
|
|
|
# Currently boids can only use the first state so these are commented out for now.
|
|
#row = layout.row()
|
|
#row.template_list(boids, "states", boids, "active_boid_state_index", compact="True")
|
|
#col = row.row()
|
|
#subrow = col.row(align=True)
|
|
#subrow.itemO("boid.boidstate_add", icon="ICON_ZOOMIN", text="")
|
|
#subrow.itemO("boid.boidstate_del", icon="ICON_ZOOMOUT", text="")
|
|
#subrow = row.row(align=True)
|
|
#subrow.itemO("boid.boidstate_move_up", icon="VICON_MOVE_UP", text="")
|
|
#subrow.itemO("boid.boidstate_move_down", icon="VICON_MOVE_DOWN", text="")
|
|
|
|
state = boids.active_boid_state
|
|
|
|
#layout.itemR(state, "name", text="State name")
|
|
|
|
row = layout.row()
|
|
row.itemR(state, "ruleset_type")
|
|
if state.ruleset_type=='FUZZY':
|
|
row.itemR(state, "rule_fuzziness", slider=True)
|
|
else:
|
|
row.itemL(text="")
|
|
|
|
row = layout.row()
|
|
row.template_list(state, "rules", state, "active_boid_rule_index")
|
|
|
|
col = row.column()
|
|
subrow = col.row()
|
|
subcol = subrow.column(align=True)
|
|
subcol.item_menu_enumO("boid.boidrule_add", "type", icon="ICON_ZOOMIN", text="")
|
|
subcol.itemO("boid.boidrule_del", icon="ICON_ZOOMOUT", text="")
|
|
subrow = col.row()
|
|
subcol = subrow.column(align=True)
|
|
subcol.itemO("boid.boidrule_move_up", icon="VICON_MOVE_UP", text="")
|
|
subcol.itemO("boid.boidrule_move_down", icon="VICON_MOVE_DOWN", text="")
|
|
|
|
rule = state.active_boid_rule
|
|
|
|
if rule:
|
|
row = layout.row()
|
|
row.itemR(rule, "name", text="")
|
|
#somebody make nice icons for boids here please! -jahka
|
|
row.itemR(rule, "in_air", icon="VICON_MOVE_UP", text="")
|
|
row.itemR(rule, "on_land", icon="VICON_MOVE_DOWN", text="")
|
|
|
|
row = layout.row()
|
|
|
|
if rule.type == 'GOAL':
|
|
row.itemR(rule, "object")
|
|
row = layout.row()
|
|
row.itemR(rule, "predict")
|
|
elif rule.type == 'AVOID':
|
|
row.itemR(rule, "object")
|
|
row = layout.row()
|
|
row.itemR(rule, "predict")
|
|
row.itemR(rule, "fear_factor")
|
|
elif rule.type == 'FOLLOW_PATH':
|
|
row.itemL(text="Not yet functional.")
|
|
elif rule.type == 'AVOID_COLLISION':
|
|
row.itemR(rule, "boids")
|
|
row.itemR(rule, "deflectors")
|
|
row.itemR(rule, "look_ahead")
|
|
elif rule.type == 'FOLLOW_LEADER':
|
|
row.itemR(rule, "object", text="")
|
|
row.itemR(rule, "distance")
|
|
row = layout.row()
|
|
row.itemR(rule, "line")
|
|
subrow = row.row()
|
|
subrow.active = rule.line
|
|
subrow.itemR(rule, "queue_size")
|
|
elif rule.type == 'AVERAGE_SPEED':
|
|
row.itemR(rule, "speed", slider=True)
|
|
row.itemR(rule, "wander", slider=True)
|
|
row.itemR(rule, "level", slider=True)
|
|
elif rule.type == 'FIGHT':
|
|
row.itemR(rule, "distance")
|
|
row.itemR(rule, "flee_distance")
|
|
|
|
|
|
class PARTICLE_PT_render(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_render"
|
|
__label__ = "Render"
|
|
|
|
def poll(self, context):
|
|
psys = context.particle_system
|
|
if psys==None: return False
|
|
if psys.settings==None: return False
|
|
return True;
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "material")
|
|
row.itemR(psys, "parent");
|
|
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
sub.itemR(part, "emitter");
|
|
sub.itemR(part, "parent");
|
|
sub = split.column()
|
|
sub.itemR(part, "unborn");
|
|
sub.itemR(part, "died");
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "ren_as", expand=True)
|
|
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
|
|
if part.ren_as == 'LINE':
|
|
sub.itemR(part, "line_length_tail")
|
|
sub.itemR(part, "line_length_head")
|
|
sub = split.column()
|
|
sub.itemR(part, "velocity_length")
|
|
elif part.ren_as == 'PATH':
|
|
|
|
if (part.type!='HAIR' and part.physics_type!='KEYED' and psys.point_cache.baked==False):
|
|
box = layout.box()
|
|
box.itemL(text="Baked or keyed particles needed for correct rendering.")
|
|
return
|
|
|
|
sub.itemR(part, "render_strand")
|
|
colsub = sub.column()
|
|
colsub.active = part.render_strand == False
|
|
colsub.itemR(part, "render_adaptive")
|
|
colsub = sub.column()
|
|
colsub.active = part.render_adaptive or part.render_strand == True
|
|
colsub.itemR(part, "adaptive_angle")
|
|
colsub = sub.column()
|
|
colsub.active = part.render_adaptive == True and part.render_strand == False
|
|
colsub.itemR(part, "adaptive_pix")
|
|
sub.itemR(part, "hair_bspline")
|
|
sub.itemR(part, "render_step", text="Steps")
|
|
sub = split.column()
|
|
|
|
sub.itemL(text="Timing:")
|
|
sub.itemR(part, "abs_path_time")
|
|
sub.itemR(part, "path_start", text="Start", slider= not part.abs_path_time)
|
|
sub.itemR(part, "path_end", text="End", slider= not part.abs_path_time)
|
|
sub.itemR(part, "random_length", text="Random", slider=True)
|
|
|
|
row = layout.row()
|
|
col = row.column()
|
|
|
|
if part.type=='HAIR' and part.render_strand==True and part.child_type=='FACES':
|
|
layout.itemR(part, "enable_simplify")
|
|
if part.enable_simplify==True:
|
|
row = layout.row()
|
|
row.itemR(part, "simplify_refsize")
|
|
row.itemR(part, "simplify_rate")
|
|
row.itemR(part, "simplify_transition")
|
|
row = layout.row()
|
|
row.itemR(part, "viewport")
|
|
subrow = row.row()
|
|
subrow.active = part.viewport==True
|
|
subrow.itemR(part, "simplify_viewport")
|
|
|
|
|
|
elif part.ren_as == 'OBJECT':
|
|
sub.itemR(part, "dupli_object")
|
|
elif part.ren_as == 'GROUP':
|
|
sub.itemR(part, "dupli_group")
|
|
split = layout.split()
|
|
sub = split.column()
|
|
sub.itemR(part, "whole_group")
|
|
sub = split.column()
|
|
colsub = sub.column()
|
|
colsub.active = part.whole_group == False
|
|
colsub.itemR(part, "rand_group")
|
|
|
|
elif part.ren_as == 'BILLBOARD':
|
|
sub.itemL(text="Align:")
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "billboard_align", expand=True)
|
|
row.itemR(part, "billboard_lock", text="Lock")
|
|
row = layout.row()
|
|
row.itemR(part, "billboard_object")
|
|
|
|
row = layout.row()
|
|
col = row.column(align=True)
|
|
col.itemL(text="Tilt:")
|
|
col.itemR(part, "billboard_tilt", text="Angle", slider=True)
|
|
col.itemR(part, "billboard_random_tilt", slider=True)
|
|
col = row.column()
|
|
col.itemR(part, "billboard_offset")
|
|
|
|
row = layout.row()
|
|
row.itemR(psys, "billboard_normal_uv")
|
|
row = layout.row()
|
|
row.itemR(psys, "billboard_time_index_uv")
|
|
|
|
row = layout.row()
|
|
row.itemL(text="Split uv's:")
|
|
row.itemR(part, "billboard_uv_split", text="Number of splits")
|
|
row = layout.row()
|
|
row.itemR(psys, "billboard_split_uv")
|
|
row = layout.row()
|
|
row.itemL(text="Animate:")
|
|
row.itemR(part, "billboard_animation", expand=True)
|
|
row.itemL(text="Offset:")
|
|
row.itemR(part, "billboard_split_offset", expand=True)
|
|
if part.ren_as == 'HALO' or part.ren_as == 'LINE' or part.ren_as=='BILLBOARD':
|
|
row = layout.row()
|
|
col = row.column()
|
|
col.itemR(part, "trail_count")
|
|
if part.trail_count > 1:
|
|
col.itemR(part, "abs_path_time", text="Length in frames")
|
|
col = row.column()
|
|
col.itemR(part, "path_end", text="Length", slider=not part.abs_path_time)
|
|
col.itemR(part, "random_length", text="Random", slider=True)
|
|
else:
|
|
col = row.column()
|
|
col.itemL(text="")
|
|
|
|
class PARTICLE_PT_draw(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_draw"
|
|
__label__ = "Display"
|
|
__default_closed__ = True
|
|
|
|
def poll(self, context):
|
|
psys = context.particle_system
|
|
if psys==None: return False
|
|
if psys.settings==None: return False
|
|
return True;
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "draw_as", expand=True)
|
|
|
|
if part.draw_as=='NONE' or (part.ren_as=='NONE' and part.draw_as=='RENDER'):
|
|
return
|
|
|
|
path = (part.ren_as=='PATH' and part.draw_as=='RENDER') or part.draw_as=='PATH'
|
|
|
|
if path and part.type!='HAIR' and part.physics_type!='KEYED' and psys.point_cache.baked==False:
|
|
box = layout.box()
|
|
box.itemL(text="Baked or keyed particles needed for correct drawing.")
|
|
return
|
|
|
|
row = layout.row()
|
|
row.itemR(part, "display", slider=True)
|
|
if part.draw_as!='RENDER' or part.ren_as=='HALO':
|
|
row.itemR(part, "draw_size")
|
|
else:
|
|
row.itemL(text="")
|
|
|
|
row = layout.row()
|
|
col = row.column()
|
|
col.itemR(part, "show_size")
|
|
col.itemR(part, "velocity")
|
|
col.itemR(part, "num")
|
|
if part.physics_type == 'BOIDS':
|
|
col.itemR(part, "draw_health")
|
|
|
|
col = row.column()
|
|
col.itemR(part, "material_color", text="Use material color")
|
|
|
|
if (path):
|
|
box = col.box()
|
|
box.itemR(part, "draw_step")
|
|
else:
|
|
subcol = col.column()
|
|
subcol.active = part.material_color==False
|
|
#subcol.itemL(text="color")
|
|
#subcol.itemL(text="Override material color")
|
|
|
|
class PARTICLE_PT_children(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_children"
|
|
__label__ = "Children"
|
|
__default_closed__ = True
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.row().itemR(part, "child_type", expand=True)
|
|
|
|
if part.child_type=='NONE':
|
|
return
|
|
|
|
row = layout.row()
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "child_nbr", text="Display")
|
|
col.itemR(part, "rendered_child_nbr", text="Render")
|
|
|
|
col = row.column(align=True)
|
|
|
|
if part.child_type=='FACES':
|
|
col.itemR(part, "virtual_parents", slider=True)
|
|
else:
|
|
col.itemR(part, "child_radius", text="Radius")
|
|
col.itemR(part, "child_roundness", text="Roundness", slider=True)
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "child_size", text="Size")
|
|
col.itemR(part, "child_random_size", text="Random")
|
|
|
|
layout.row().itemL(text="Effects:")
|
|
|
|
row = layout.row()
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "clump_factor", slider=True)
|
|
col.itemR(part, "clumppow", slider=True)
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "rough_endpoint")
|
|
col.itemR(part, "rough_end_shape")
|
|
|
|
row = layout.row()
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "rough1")
|
|
col.itemR(part, "rough1_size")
|
|
|
|
col = row.column(align=True)
|
|
col.itemR(part, "rough2")
|
|
col.itemR(part, "rough2_size")
|
|
col.itemR(part, "rough2_thres", slider=True)
|
|
|
|
row = layout.row()
|
|
col = row.column(align=True)
|
|
col.itemR(part, "child_length", slider=True)
|
|
col.itemR(part, "child_length_thres", slider=True)
|
|
|
|
col = row.column(align=True)
|
|
col.itemL(text="Space reserved for")
|
|
col.itemL(text="hair parting controls")
|
|
|
|
layout.row().itemL(text="Kink:")
|
|
layout.row().itemR(part, "kink", expand=True)
|
|
|
|
split = layout.split()
|
|
|
|
sub = split.column()
|
|
sub.itemR(part, "kink_amplitude")
|
|
sub.itemR(part, "kink_frequency")
|
|
sub = split.column()
|
|
sub.itemR(part, "kink_shape", slider=True)
|
|
|
|
class PARTICLE_PT_effectors(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_effectors"
|
|
__label__ = "Effectors"
|
|
__default_closed__ = True
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.itemR(part, "effector_group")
|
|
|
|
layout.itemR(part, "eweight_all", slider=True)
|
|
|
|
layout.itemS()
|
|
layout.itemR(part, "eweight_spherical", slider=True)
|
|
layout.itemR(part, "eweight_vortex", slider=True)
|
|
layout.itemR(part, "eweight_magnetic", slider=True)
|
|
layout.itemR(part, "eweight_wind", slider=True)
|
|
layout.itemR(part, "eweight_curveguide", slider=True)
|
|
layout.itemR(part, "eweight_texture", slider=True)
|
|
layout.itemR(part, "eweight_harmonic", slider=True)
|
|
layout.itemR(part, "eweight_charge", slider=True)
|
|
layout.itemR(part, "eweight_lennardjones", slider=True)
|
|
|
|
class PARTICLE_PT_vertexgroups(ParticleButtonsPanel):
|
|
__idname__= "PARTICLE_PT_vertexgroups"
|
|
__label__ = "Vertexgroups"
|
|
__default_closed__ = True
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
psys = context.particle_system
|
|
part = psys.settings
|
|
|
|
layout.itemL(text="Nothing here yet.")
|
|
|
|
#row = layout.row()
|
|
#row.itemL(text="Vertex Group")
|
|
#row.itemL(text="Negate")
|
|
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_density")
|
|
#row.itemR(psys, "vertex_group_density_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_velocity")
|
|
#row.itemR(psys, "vertex_group_velocity_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_length")
|
|
#row.itemR(psys, "vertex_group_length_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_clump")
|
|
#row.itemR(psys, "vertex_group_clump_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_kink")
|
|
#row.itemR(psys, "vertex_group_kink_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_roughness1")
|
|
#row.itemR(psys, "vertex_group_roughness1_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_roughness2")
|
|
#row.itemR(psys, "vertex_group_roughness2_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_roughness_end")
|
|
#row.itemR(psys, "vertex_group_roughness_end_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_size")
|
|
#row.itemR(psys, "vertex_group_size_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_tangent")
|
|
#row.itemR(psys, "vertex_group_tangent_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_rotation")
|
|
#row.itemR(psys, "vertex_group_rotation_negate", text="")
|
|
|
|
#row = layout.row()
|
|
#row.itemR(psys, "vertex_group_field")
|
|
#row.itemR(psys, "vertex_group_field_negate", text="")
|
|
|
|
bpy.types.register(PARTICLE_PT_particles)
|
|
bpy.types.register(PARTICLE_PT_cache)
|
|
bpy.types.register(PARTICLE_PT_emission)
|
|
bpy.types.register(PARTICLE_PT_initial)
|
|
bpy.types.register(PARTICLE_PT_physics)
|
|
bpy.types.register(PARTICLE_PT_boidbrain)
|
|
bpy.types.register(PARTICLE_PT_render)
|
|
bpy.types.register(PARTICLE_PT_draw)
|
|
bpy.types.register(PARTICLE_PT_children)
|
|
bpy.types.register(PARTICLE_PT_effectors)
|
|
bpy.types.register(PARTICLE_PT_vertexgroups)
|