diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 9ebcb2adf98..c5723ea4c71 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -1167,8 +1167,12 @@ class PARTICLE_PT_children(ParticleButtonsPanel, Panel): col.label(text="Effects:") sub = col.column(align=True) - sub.prop(part, "clump_factor", slider=True) - sub.prop(part, "clump_shape", slider=True) + sub.prop(part, "use_clump_curve") + if part.use_clump_curve: + sub.template_curve_mapping(part, "clump_curve") + else: + sub.prop(part, "clump_factor", slider=True) + sub.prop(part, "clump_shape", slider=True) sub = col.column(align=True) sub.prop(part, "child_length", slider=True) diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 54901448563..cb766082467 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -330,7 +330,7 @@ void psys_find_parents(struct ParticleSimulationData *sim); void psys_cache_paths(struct ParticleSimulationData *sim, float cfra); void psys_cache_edit_paths(struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra); void psys_cache_child_paths(struct ParticleSimulationData *sim, float cfra, int editupdate); -int do_guides(struct ListBase *effectors, ParticleKey *state, int pa_num, float time); +int do_guides(struct ParticleSettings *part, struct ListBase *effectors, ParticleKey *state, int pa_num, float time); void precalc_guides(struct ParticleSimulationData *sim, struct ListBase *effectors); float psys_get_timestep(struct ParticleSimulationData *sim); float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *birthtime, float *dietime); @@ -339,6 +339,7 @@ void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, s int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always); /* child paths */ +void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part); void psys_apply_child_modifiers(struct ParticleThreadContext *ctx, struct ListBase *modifiers, struct ChildParticle *cpa, struct ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4], struct ParticleCacheKey *keys, struct ParticleCacheKey *parent_keys); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index efcc0ebca21..1001dde2561 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -64,6 +64,7 @@ #include "BKE_boids.h" #include "BKE_cloth.h" +#include "BKE_colortools.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_group.h" @@ -373,6 +374,10 @@ void BKE_particlesettings_free(ParticleSettings *part) MTex *mtex; int a; BKE_free_animdata(&part->id); + + if (part->clumpcurve) + curvemapping_free(part->clumpcurve); + free_partdeflect(part->pd); free_partdeflect(part->pd2); @@ -1665,7 +1670,7 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in extern void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start); -extern float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump); +extern float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve); void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) { @@ -1708,7 +1713,7 @@ void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) } } -int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) +int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, int index, float time) { EffectorCache *eff; PartDeflect *pd; @@ -1777,10 +1782,14 @@ int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) mul_v3_fl(vec_to_point, radius); } } + + if (part->clumpcurve) + curvemapping_changed_all(part->clumpcurve); + par.co[0] = par.co[1] = par.co[2] = 0.0f; copy_v3_v3(key.co, vec_to_point); do_kink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); - do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f); + do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f, part->clumpcurve); copy_v3_v3(vec_to_point, key.co); add_v3_v3(vec_to_point, guidevec); @@ -1979,6 +1988,10 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi if (psys->part->flag & PART_CHILD_EFFECT) ctx->vg_effector = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_EFFECTOR); + /* prepare curvemapping tables */ + if (part->clumpcurve) + curvemapping_changed_all(part->clumpcurve); + return true; } @@ -2545,7 +2558,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) if (sim->psys->effectors && (psys->part->flag & PART_CHILD_EFFECT) == 0) { for (k = 0, ca = cache[p]; k <= steps; k++, ca++) /* ca is safe to cast, since only co and vel are used */ - do_guides(sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)steps); + do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)steps); } /* lattices have to be calculated separately to avoid mixups between effector calculations */ @@ -3141,6 +3154,18 @@ ParticleSettings *psys_new_settings(const char *name, Main *main) return part; } +void BKE_particlesettings_clump_curve_init(ParticleSettings *part) +{ + CurveMapping *cumap = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + cumap->cm[0].curve[0].x = 0.0f; + cumap->cm[0].curve[0].y = 1.0f; + cumap->cm[0].curve[1].x = 1.0f; + cumap->cm[0].curve[1].y = 1.0f; + + part->clumpcurve = cumap; +} + ParticleSettings *BKE_particlesettings_copy(ParticleSettings *part) { ParticleSettings *partn; @@ -3152,6 +3177,9 @@ ParticleSettings *BKE_particlesettings_copy(ParticleSettings *part) partn->effector_weights = MEM_dupallocN(part->effector_weights); partn->fluid = MEM_dupallocN(part->fluid); + if (part->clumpcurve) + partn->clumpcurve = curvemapping_copy(part->clumpcurve); + partn->boids = boid_copy_settings(part->boids); for (a = 0; a < MAX_MTEX; a++) { @@ -3604,7 +3632,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * mul_mat3_m4_v3(hairmat, state->vel); if (sim->psys->effectors && (part->flag & PART_CHILD_GUIDE) == 0) { - do_guides(sim->psys->effectors, state, p, state->time); + do_guides(sim->psys->part, sim->psys->effectors, state, p, state->time); /* TODO: proper velocity handling */ } diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index fb261b248dc..68e11c9368e 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -34,13 +34,14 @@ #include "DNA_material_types.h" +#include "BKE_colortools.h" #include "BKE_particle.h" struct Material; void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start); -float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump); +float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve); void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, ParticleKey *par, float *par_rot, ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t); @@ -359,11 +360,19 @@ void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, f copy_v3_v3(state->co, result); } -float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) +float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve) { float clump = 0.f; - if (par && clumpfac != 0.0f) { + if (!par) + return 0.0f; + + if (clumpcurve) { + clump = pa_clump * (1.0f - CLAMPIS(curvemapping_evaluateF(clumpcurve, 0, time), 0.0f, 1.0f)); + + interp_v3_v3v3(state->co, state->co, par->co, clump); + } + else if (clumpfac != 0.0f) { float cpow; if (clumppow < 0.0f) @@ -439,10 +448,10 @@ void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, Part if (part->flag & PART_CHILD_EFFECT) /* state is safe to cast, since only co and vel are used */ - guided = do_guides(sim->psys->effectors, (ParticleKey *)state, cpa->parent, t); + guided = do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)state, cpa->parent, t); if (guided == 0) { - float clump = do_clump(state, par, t, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f); + float clump = do_clump(state, par, t, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f, part->clumpcurve); if (kink_freq != 0.f) { float kink_amp = part->kink_amp * (1.f - part->kink_amp_clump * clump); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 6e7e341e1cc..8c0840ad651 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2089,7 +2089,7 @@ static void basic_integrate(ParticleSimulationData *sim, int p, float dfra, floa tkey.time=pa->state.time; if (part->type != PART_HAIR) { - if (do_guides(sim->psys->effectors, &tkey, p, time)) { + if (do_guides(sim->psys->part, sim->psys->effectors, &tkey, p, time)) { copy_v3_v3(pa->state.co,tkey.co); /* guides don't produce valid velocity */ sub_v3_v3v3(pa->state.vel, tkey.co, pa->prev_state.co); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3b2179e9c47..10bdfbd70e7 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3816,6 +3816,10 @@ static void direct_link_particlesettings(FileData *fd, ParticleSettings *part) direct_link_partdeflect(part->pd); direct_link_partdeflect(part->pd2); + part->clumpcurve = newdataadr(fd, part->clumpcurve); + if (part->clumpcurve) + direct_link_curvemapping(fd, part->clumpcurve); + part->effector_weights = newdataadr(fd, part->effector_weights); if (!part->effector_weights) part->effector_weights = BKE_add_effector_weights(part->eff_group); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index adb37aaab71..81f4c16cea1 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1108,6 +1108,9 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase) writestruct(wd, DATA, "PartDeflect", 1, part->pd2); writestruct(wd, DATA, "EffectorWeights", 1, part->effector_weights); + if (part->clumpcurve) + write_curvemapping(wd, part->clumpcurve); + dw = part->dupliweights.first; for (; dw; dw=dw->next) { /* update indices */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 0b1f85c043a..6d119b039bd 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -233,6 +233,7 @@ typedef struct ParticleSettings { int trail_count; /* keyed particles */ int keyed_loops; + struct CurveMapping *clumpcurve; /* hair dynamics */ float bending_random; diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 37201eca5f6..5e114a51ceb 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -46,6 +46,8 @@ #include "DNA_material_types.h" #include "DNA_movieclip_types.h" #include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_sequence_types.h" #include "MEM_guardedalloc.h" @@ -345,6 +347,13 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * WM_main_add_notifier(NC_LINESTYLE, linestyle); break; } + case ID_PA: + { + ParticleSettings *part = ptr->id.data; + + DAG_id_tag_update(&part->id, OB_RECALC_DATA | PSYS_RECALC_REDO); + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, part); + } default: break; } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 6d411cf7608..6540604199d 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -131,6 +131,7 @@ static EnumPropertyItem part_hair_ren_as_items[] = { #include "BKE_context.h" #include "BKE_cloth.h" +#include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" @@ -874,6 +875,29 @@ static int rna_PartSettings_is_fluid_get(PointerRNA *ptr) return part->type == PART_FLUID; } +int rna_ParticleSettings_use_clump_curve_get(PointerRNA *ptr) +{ + ParticleSettings *part = ptr->data; + return part->clumpcurve != NULL; +} + +void rna_ParticleSettings_use_clump_curve_set(PointerRNA *ptr, int value) +{ + ParticleSettings *part = ptr->data; + + if (!value) { + if (part->clumpcurve) { + curvemapping_free(part->clumpcurve); + part->clumpcurve = NULL; + } + } + else { + if (!part->clumpcurve) { + BKE_particlesettings_clump_curve_init(part); + } + } +} + static void rna_ParticleSystem_name_set(PointerRNA *ptr, const char *value) { Object *ob = ptr->id.data; @@ -2797,6 +2821,17 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shape", "Shape of clumping"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "use_clump_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_ParticleSettings_use_clump_curve_get", "rna_ParticleSettings_use_clump_curve_set"); + RNA_def_property_ui_text(prop, "Use Clump Curve", "Use a curve to define clump tapering"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "clump_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "clumpcurve"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Clump Curve", "Curve defining clump tapering"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); /* kink */ prop = RNA_def_property(srna, "kink_amplitude", PROP_FLOAT, PROP_NONE);