Another crappy approach to spirals on hairs, crazy expensive though.

Conflicts:
	source/blender/blenkernel/intern/particle.c
This commit is contained in:
Lukas Tönne 2015-01-13 17:24:20 +01:00
parent 9bf7633936
commit 307c4a4df9
12 changed files with 316 additions and 198 deletions

@ -1233,10 +1233,10 @@ class PARTICLE_PT_children(ParticleButtonsPanel, Panel):
if part.kink in {'SPIRAL'}:
col = split.column()
col.prop(part, "kink_amplitude", text="Radius")
col.prop(part, "kink_flat", text="Start", slider=True)
col = split.column(align=True)
col.prop(part, "kink_frequency", text="Frequency")
col.prop(part, "kink_shape", text="Shape", slider=True)
col.prop(part, "kink_extra_steps", text="Steps")
else:
col = split.column()
sub = col.column(align=True)

@ -126,7 +126,7 @@ typedef struct ParticleCacheKey {
float rot[4];
float col[3];
float time;
int steps;
int segments;
} ParticleCacheKey;
typedef struct ParticleThreadContext {
@ -150,7 +150,7 @@ typedef struct ParticleThreadContext {
struct ParticleData *tpars;
/* path caching */
int editupdate, between, steps;
int editupdate, between, segments, extra_segments;
int totchild, totparent, parent_pass;
float cfra;

@ -970,8 +970,8 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* some hair paths might be non-existent so they can't be used for duplication */
if (hair &&
((a < totpart && psys->pathcache[a]->steps < 0) ||
(a >= totpart && psys->childcache[a - totpart]->steps < 0)))
((a < totpart && psys->pathcache[a]->segments < 0) ||
(a >= totpart && psys->childcache[a - totpart]->segments < 0)))
{
continue;
}

@ -105,7 +105,7 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread
ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex);
extern void do_child_modifiers(ParticleSimulationData *sim,
ParticleTexture *ptex, ParticleKey *par, float *par_rot, const float par_orco[3],
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t, float spiral_start[3], float *time_prev, float *co_prev);
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t);
/* few helpers for countall etc. */
int count_particles(ParticleSystem *psys)
@ -139,7 +139,7 @@ int count_particles_mod(ParticleSystem *psys, int totgr, int cur)
#define PATH_CACHE_BUF_SIZE 1024
static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int steps)
static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int totkeys)
{
LinkData *buf;
ParticleCacheKey **cache;
@ -152,10 +152,10 @@ static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot,
while (totkey < tot) {
totbufkey = MIN2(tot - totkey, PATH_CACHE_BUF_SIZE);
buf = MEM_callocN(sizeof(LinkData), "PathCacheLinkData");
buf->data = MEM_callocN(sizeof(ParticleCacheKey) * totbufkey * steps, "ParticleCacheKey");
buf->data = MEM_callocN(sizeof(ParticleCacheKey) * totbufkey * totkeys, "ParticleCacheKey");
for (i = 0; i < totbufkey; i++)
cache[totkey + i] = ((ParticleCacheKey *)buf->data) + i * steps;
cache[totkey + i] = ((ParticleCacheKey *)buf->data) + i * totkeys;
totkey += totbufkey;
BLI_addtail(bufs, buf);
@ -1139,9 +1139,9 @@ static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCach
ParticleCacheKey *cur = first;
/* scale the requested time to fit the entire path even if the path is cut early */
t *= (first + first->steps)->time;
t *= (first + first->segments)->time;
while (i < first->steps && cur->time < t)
while (i < first->segments && cur->time < t)
cur++;
if (cur->time == t)
@ -1671,7 +1671,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, float spiral_start[3], float *time_prev, float *co_prev);
short type, short axis, float obmat[4][4], int smooth_start);
extern float do_clump(ParticleKey *state, ParticleKey *par, float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
@ -1793,11 +1793,10 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i
{
ParticleKey key, par;
float orco_offset[3] = {0.0f, 0.0f, 0.0f};
float spiral_start[3], time_prev = 0.0f, co_prev[3] = {0,0,0};
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, spiral_start, &time_prev, co_prev);
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, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f,
part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, part->clumpcurve);
copy_v3_v3(vec_to_point, key.co);
@ -1943,7 +1942,7 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
int totparent = 0, between = 0;
int steps = 1 << part->draw_step;
int segments = 1 << part->draw_step;
int totchild = psys->totchild;
psys_thread_context_init(ctx, sim);
@ -1955,7 +1954,7 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi
if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
totchild = 0;
steps = 1 << pset->draw_step;
segments = 1 << pset->draw_step;
}
if (totchild && part->childtype == PART_CHILD_FACES) {
@ -1969,7 +1968,7 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi
}
if (psys->renderdata)
steps = (int)pow(2.0, (double)part->ren_step);
segments = 1 << part->ren_step;
else {
totchild = (int)((float)totchild * (float)part->disp / 100.0f);
totparent = MIN2(totparent, totchild);
@ -1980,7 +1979,11 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi
/* fill context values */
ctx->between = between;
ctx->steps = steps;
ctx->segments = segments;
if (ELEM(part->kink, PART_KINK_SPIRAL))
ctx->extra_segments = max_ii(part->kink_extra_steps, 1);
else
ctx->extra_segments = 0;
ctx->totchild = totchild;
ctx->totparent = totparent;
ctx->parent_pass = 0;
@ -2060,7 +2063,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
if (!needupdate)
return;
else
memset(child_keys, 0, sizeof(*child_keys) * (ctx->steps + 1));
memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
}
/* get parent paths */
@ -2083,14 +2086,14 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
if (part->flag & PART_CHILD_LONG_HAIR) {
/* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */
float d1 = len_v3v3(key[0]->co, key[w]->co);
float d2 = len_v3v3((key[0] + key[0]->steps - 1)->co, (key[w] + key[w]->steps - 1)->co);
float d2 = len_v3v3((key[0] + key[0]->segments - 1)->co, (key[w] + key[w]->segments - 1)->co);
d = d1 > 0.f ? d2 / d1 - 1.f : 10000.f;
}
else {
float v1[3], v2[3];
sub_v3_v3v3(v1, (key[0] + key[0]->steps - 1)->co, key[0]->co);
sub_v3_v3v3(v2, (key[w] + key[w]->steps - 1)->co, key[w]->co);
sub_v3_v3v3(v1, (key[0] + key[0]->segments - 1)->co, key[0]->co);
sub_v3_v3v3(v2, (key[w] + key[w]->segments - 1)->co, key[w]->co);
normalize_v3(v1);
normalize_v3(v2);
@ -2138,7 +2141,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC))
return;
memset(child_keys, 0, sizeof(*child_keys) * (ctx->steps + 1));
memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
}
/* get the parent path */
@ -2157,18 +2160,18 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat);
}
child_keys->steps = ctx->steps;
child_keys->segments = ctx->segments;
/* get different child parameters from textures & vgroups */
get_child_modifier_parameters(part, ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex);
if (ptex.exist < psys_frand(psys, i + 24)) {
child_keys->steps = -1;
child_keys->segments = -1;
return;
}
/* create the child path */
for (k = 0, child = child_keys; k <= ctx->steps; k++, child++) {
for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) {
if (ctx->between) {
int w = 0;
@ -2186,7 +2189,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
/* Fade the effect of rotation for even lengths in the end */
project_v3_v3v3(dvec, off2[w], (key[w] + k)->vel);
madd_v3_v3fl(off2[w], dvec, -(float)k / (float)ctx->steps);
madd_v3_v3fl(off2[w], dvec, -(float)k / (float)ctx->segments);
}
add_v3_v3(off2[w], (key[w] + k)->co);
@ -2210,14 +2213,14 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
offset_child(cpa, (ParticleKey *)(key[0] + k), par_rot, (ParticleKey *)child, part->childflat, part->childrad);
}
child->time = (float)k / (float)ctx->steps;
child->time = (float)k / (float)ctx->segments;
}
/* apply effectors */
if (part->flag & PART_CHILD_EFFECT) {
for (k = 0, child = child_keys; k <= ctx->steps; k++, child++) {
for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) {
if (k) {
do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->steps, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec);
do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->segments, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec);
}
else {
sub_v3_v3v3(eff_vec, (child + 1)->co, child->co);
@ -2313,7 +2316,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
/* Hide virtual parents */
if (i < ctx->totparent)
child_keys->steps = -1;
child_keys->segments = -1;
}
static void exec_child_path_cache(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid))
@ -2358,7 +2361,8 @@ void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupd
else {
/* clear out old and create new empty path cache */
free_child_path_cache(sim->psys);
sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx.steps + 1);
sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx.segments + ctx.extra_segments + 1);
sim->psys->totchildcache = totchild;
}
@ -2459,7 +2463,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
float prev_tangent[3] = {0.0f, 0.0f, 0.0f}, hairmat[4][4];
float rotmat[3][3];
int k;
int steps = (int)pow(2.0, (double)(psys->renderdata ? part->ren_step : part->draw_step));
int segments = (int)pow(2.0, (double)(psys->renderdata ? part->ren_step : part->draw_step));
int totpart = psys->totpart;
float length, vec[3];
float *vg_effector = NULL;
@ -2479,7 +2483,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
/* clear out old and create new empty path cache */
psys_free_path_cache(psys, psys->edit);
cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, steps + 1);
cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1);
psys->lattice_deform_data = psys_create_lattice_deform_data(sim);
ma = give_current_material(sim->ob, psys->part->omat);
@ -2514,9 +2518,9 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE);
pind.dm = hair_dm;
memset(cache[p], 0, sizeof(*cache[p]) * (steps + 1));
memset(cache[p], 0, sizeof(*cache[p]) * (segments + 1));
cache[p]->steps = steps;
cache[p]->segments = segments;
/*--get the first data points--*/
init_particle_interpolation(sim->ob, sim->psys, pa, &pind);
@ -2538,15 +2542,15 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
}
if (birthtime >= dietime) {
cache[p]->steps = -1;
cache[p]->segments = -1;
continue;
}
dietime = birthtime + pa_length * (dietime - birthtime);
/*--interpolate actual path from data points--*/
for (k = 0, ca = cache[p]; k <= steps; k++, ca++) {
time = (float)k / (float)steps;
for (k = 0, ca = cache[p]; k <= segments; k++, ca++) {
time = (float)k / (float)segments;
t = birthtime + time * (dietime - birthtime);
result.time = -t;
do_particle_interpolation(psys, p, pa, t, &pind, &result);
@ -2582,29 +2586,29 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
sub_v3_v3v3(vec, (cache[p] + 1)->co, cache[p]->co);
length = len_v3(vec);
for (k = 1, ca = cache[p] + 1; k <= steps; k++, ca++)
do_path_effectors(sim, p, ca, k, steps, cache[p]->co, effector, dfra, cfra, &length, vec);
for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++)
do_path_effectors(sim, p, ca, k, segments, cache[p]->co, effector, dfra, cfra, &length, vec);
}
/* apply guide curves to path data */
if (sim->psys->effectors && (psys->part->flag & PART_CHILD_EFFECT) == 0) {
for (k = 0, ca = cache[p]; k <= steps; k++, ca++)
for (k = 0, ca = cache[p]; k <= segments; k++, ca++)
/* ca is safe to cast, since only co and vel are used */
do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)steps);
do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)segments);
}
/* lattices have to be calculated separately to avoid mixups between effector calculations */
if (psys->lattice_deform_data) {
for (k = 0, ca = cache[p]; k <= steps; k++, ca++)
for (k = 0, ca = cache[p]; k <= segments; k++, ca++)
calc_latt_deform(psys->lattice_deform_data, ca->co, 1.0f);
}
}
/* finally do rotation & velocity */
for (k = 1, ca = cache[p] + 1; k <= steps; k++, ca++) {
for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++) {
cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k);
if (k == steps)
if (k == segments)
copy_qt_qt(ca->rot, (ca - 1)->rot);
/* set velocity */
@ -2613,7 +2617,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
if (k == 1)
copy_v3_v3((ca - 1)->vel, ca->vel);
ca->time = (float)k / (float)steps;
ca->time = (float)k / (float)segments;
}
/* First rotation is based on emitting face orientation.
* This is way better than having flipping rotations resulting
@ -2657,17 +2661,17 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
float t, time = 0.0f, keytime = 0.0f /*, frs_sec */;
float hairmat[4][4], rotmat[3][3], prev_tangent[3] = {0.0f, 0.0f, 0.0f};
int k, i;
int steps = (int)pow(2.0, (double)pset->draw_step);
int segments = 1 << pset->draw_step;
int totpart = edit->totpoint, recalc_set = 0;
float sel_col[3];
float nosel_col[3];
steps = MAX2(steps, 4);
segments = MAX2(segments, 4);
if (!cache || edit->totpoint != edit->totcached) {
/* clear out old and create new empty path cache */
psys_free_path_cache(edit->psys, edit);
cache = edit->pathcache = psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, steps + 1);
cache = edit->pathcache = psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, segments + 1);
/* set flag for update (child particles check this too) */
for (i = 0, point = edit->points; i < totpart; i++, point++)
@ -2714,9 +2718,9 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
}
memset(cache[i], 0, sizeof(*cache[i]) * (steps + 1));
memset(cache[i], 0, sizeof(*cache[i]) * (segments + 1));
cache[i]->steps = steps;
cache[i]->segments = segments;
/*--get the first data points--*/
init_particle_interpolation(ob, psys, pa, &pind);
@ -2732,13 +2736,13 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
dietime = pind.dietime;
if (birthtime >= dietime) {
cache[i]->steps = -1;
cache[i]->segments = -1;
continue;
}
/*--interpolate actual path from data points--*/
for (k = 0, ca = cache[i]; k <= steps; k++, ca++) {
time = (float)k / (float)steps;
for (k = 0, ca = cache[i]; k <= segments; k++, ca++) {
time = (float)k / (float)segments;
t = birthtime + time * (dietime - birthtime);
result.time = -t;
do_particle_interpolation(psys, i, pa, t, &pind, &result);
@ -2751,7 +2755,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
if (k) {
cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k);
if (k == steps)
if (k == segments)
copy_qt_qt(ca->rot, (ca - 1)->rot);
/* set velocity */
@ -2785,7 +2789,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
/* at the moment this is only used for weight painting.
* will need to move out of this check if its used elsewhere. */
t2 = birthtime + ((float)k / (float)steps) * (dietime - birthtime);
t2 = birthtime + ((float)k / (float)segments) * (dietime - birthtime);
while (pind.hkey[1]->time < t2) pind.hkey[1]++;
pind.hkey[0] = pind.hkey[1] - 1;
@ -3119,6 +3123,7 @@ static void default_particle_settings(ParticleSettings *part)
part->adapt_pix = 3;
part->kink_axis = 2;
part->kink_amp_clump = 1.f;
part->kink_extra_steps = 4;
part->clump_noise_size = 1.0f;
part->reactevent = PART_EVENT_DEATH;
part->disp = 100;
@ -3817,10 +3822,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *
copy_particle_key(&tstate, state, 1);
/* apply different deformations to the child path */
{
float spiral_start[3], time_prev = 0.0f, co_prev[3] = {0,0,0};
do_child_modifiers(sim, &ptex, par, par->rot, par_orco, cpa, orco, hairmat, state, t, spiral_start, &time_prev, co_prev);
}
do_child_modifiers(sim, &ptex, par, par->rot, par_orco, cpa, orco, hairmat, state, t);
/* try to estimate correct velocity */
if (vel) {
@ -3916,7 +3918,6 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
ParticleKey *key1;
float t = (cfra - pa->time) / pa->lifetime;
float par_orco[3] = {0.0f, 0.0f, 0.0f};
float spiral_start[3], time_prev = 0.0f, co_prev[3] = {0,0,0};
key1 = &pa->state;
offset_child(cpa, key1, key1->rot, state, part->childflat, part->childrad);
@ -3924,7 +3925,7 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
CLAMP(t, 0.0f, 1.0f);
unit_m4(mat);
do_child_modifiers(sim, NULL, key1, key1->rot, par_orco, cpa, cpa->fuv, mat, state, t, spiral_start, &time_prev, co_prev);
do_child_modifiers(sim, NULL, key1, key1->rot, par_orco, cpa, cpa->fuv, mat, state, t);
if (psys->lattice_deform_data)
calc_latt_deform(psys->lattice_deform_data, state->co, 1.0f);
@ -4052,7 +4053,7 @@ void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa
float loc[3], nor[3], vec[3], side[3], len;
float xvec[3] = {-1.0, 0.0, 0.0}, nmat[3][3];
sub_v3_v3v3(vec, (cache + cache->steps)->co, cache->co);
sub_v3_v3v3(vec, (cache + cache->segments)->co, cache->co);
len = normalize_v3(vec);
if (pa == NULL && psys->part->childflat != PART_CHILD_FACES)

@ -40,12 +40,12 @@
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 spiral_start[3], float *time_prev, float *co_prev);
short type, short axis, float obmat[4][4], int smooth_start);
float do_clump(ParticleKey *state, ParticleKey *par, float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
void do_child_modifiers(ParticleSimulationData *sim,
ParticleTexture *ptex, ParticleKey *par, float *par_rot, const float par_orco[3],
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t, float spiral_start[3], float *time_prev, float *co_prev);
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t);
static void get_strand_normal(Material *ma, const float surfnor[3], float surfdist, float nor[3])
{
@ -120,13 +120,171 @@ typedef struct ParticlePathModifier {
/* ------------------------------------------------------------------------- */
static void do_kink_spiral_deform(ParticleKey *state, const float dir[3],
float time, float freq, float shape, float amplitude,
const float spiral_start[3])
{
float kink[3] = {1.f, 0.f, 0.f};
float result[3];
CLAMP(time, 0.f, 1.f);
#if 0
{
float temp[3];
kink[axis] = 1.f;
if (obmat)
mul_mat3_m4_v3(obmat, kink);
mul_qt_v3(par_rot, kink);
/* make sure kink is normal to strand */
project_v3_v3v3(temp, kink, par_vel);
sub_v3_v3(kink, temp);
normalize_v3(kink);
}
#endif
copy_v3_v3(result, state->co);
{
/* Creates a logarithmic spiral:
* r(theta) = a * exp(b * theta)
*
* The "density" parameter b is defined by the shape parameter
* and goes up to the Golden Spiral for 1.0
* http://en.wikipedia.org/wiki/Golden_spiral
*/
const float b = shape * (1.0f + sqrtf(5.0f)) / M_PI * 0.25f;
/* angle of the spiral against the curve (rotated opposite to make a smooth transition) */
const float start_angle = (b != 0.0f ? atanf(1.0f / b) : -M_PI*0.5f) + (b > 0.0f ? -M_PI*0.5f : M_PI*0.5f);
float up[3], rot[3][3];
float vec[3];
float theta = freq * time * 2.0f*M_PI;
float radius = amplitude * expf(b * theta);
cross_v3_v3v3(up, dir, kink);
mul_v3_v3fl(vec, up, radius);
axis_angle_normalized_to_mat3(rot, kink, theta);
mul_m3_v3(rot, vec);
madd_v3_v3fl(vec, up, -amplitude);
axis_angle_normalized_to_mat3(rot, kink, -start_angle);
mul_m3_v3(rot, vec);
add_v3_v3v3(result, spiral_start, vec);
}
copy_v3_v3(state->co, result);
}
static void do_kink_spiral(ParticleThreadContext *ctx, ParticleTexture *ptex, const float parent_orco[3],
ChildParticle *cpa, const float orco[3], float hairmat[4][4],
ParticleCacheKey *keys, int *r_totkeys, float *r_max_length)
{
struct ParticleSettings *part = ctx->sim.psys->part;
const int totkeys = ctx->segments + 1;
const int extrakeys = ctx->extra_segments;
float kink_amp = part->kink_amp;
float kink_freq = part->kink_freq;
float kink_shape = part->kink_shape;
float rough1 = part->rough1;
float rough2 = part->rough2;
float rough_end = part->rough_end;
ParticlePathIterator iter;
ParticleCacheKey *key;
int k;
float dir[3];
float spiral_start[3];
float len, totlen, cutlen;
int start_index = 0, end_index = 0;
if (ptex) {
kink_freq *= ptex->kink;
rough1 *= ptex->rough1;
rough2 *= ptex->rough2;
rough_end *= ptex->roughe;
}
totlen = 0.0f;
for (k = 0, key = keys; k < totkeys-1; k++, key++)
totlen += len_v3v3((key+1)->co, key->co);
cutlen = totlen - kink_amp;
zero_v3(spiral_start);
len = 0.0f;
for (k = 0, key = keys; k < totkeys-1; k++, key++) {
float seglen = len_v3v3((key+1)->co, key->co);
if (seglen > 0.0f && len + seglen >= cutlen) {
float fac = (cutlen - len) / seglen;
start_index = k + 1;
end_index = start_index + extrakeys;
interp_v3_v3v3(spiral_start, key->co, (key+1)->co, fac);
break;
}
len += seglen;
}
#if 0
/* use the last segment's direction for the spiral orientation
* this is more consistent and stable than the last flat segment
* when changing the amplitude
*/
if (totkeys > 1) {
sub_v3_v3v3(dir, (keys+totkeys-1)->co, (keys+totkeys-2)->co);
normalize_v3(dir);
}
else
zero_v3(dir);
#endif
zero_v3(dir);
for (k = 0, key = keys; k < end_index; k++, key++) {
psys_path_iter_get(&iter, keys, end_index, NULL, k);
if (k < start_index) {
sub_v3_v3v3(dir, (key+1)->co, key->co);
normalize_v3(dir);
}
else {
float spiral_time = (float)(k - start_index) / (float)(extrakeys-1);
do_kink_spiral_deform((ParticleKey *)key, dir, spiral_time, kink_freq, kink_shape, kink_amp, spiral_start);
}
/* apply different deformations to the child path */
/* XXX TODO does not work without parent key, replace by explicit loc, rot, dir arguments! */
do_child_modifiers(&ctx->sim, ptex, (ParticleKey *)iter.parent_key, iter.parent_rotation, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, iter.time);
}
totlen = 0.0f;
for (k = 0, key = keys; k < end_index-1; k++, key++)
totlen += len_v3v3((key+1)->co, key->co);
*r_totkeys = end_index;
*r_max_length = totlen;
}
/* ------------------------------------------------------------------------- */
static bool check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *key, float max_length, float step_length, float *cur_length, float dvec[3])
{
if (*cur_length + step_length > max_length) {
sub_v3_v3v3(dvec, key->co, (key-1)->co);
mul_v3_fl(dvec, (max_length - *cur_length) / step_length);
add_v3_v3v3(key->co, (key-1)->co, dvec);
keys->steps = k;
keys->segments = k;
/* something over the maximum step value */
return false;
}
@ -144,14 +302,10 @@ void psys_apply_child_modifiers(ParticleThreadContext *ctx, struct ListBase *mod
struct Material *ma = ctx->ma;
const bool draw_col_ma = (part->draw_col == PART_DRAW_COL_MAT);
const int totkeys = ctx->steps + 1;
const float step_length = 1.0f / (float)ctx->steps;
const float max_length = ptex->length;
ParticlePathModifier *mod;
ParticleCacheKey *key;
int k;
float cur_length;
int totkeys, k;
float max_length;
#if 0 /* TODO for the future: use true particle modifiers that work on the whole curve */
for (mod = modifiers->first; mod; mod = mod->next) {
@ -161,65 +315,73 @@ void psys_apply_child_modifiers(ParticleThreadContext *ctx, struct ListBase *mod
(void)modifiers;
(void)mod;
{
if (part->kink == PART_KINK_SPIRAL) {
do_kink_spiral(ctx, ptex, parent_orco, cpa, orco, hairmat, keys, &totkeys, &max_length);
keys->segments = totkeys - 1;
}
else {
ParticlePathIterator iter;
float spiral_start[3];
float time_prev = 0.0f;
float co_prev[3] = {0.0f, 0.0f, 0.0f};
totkeys = ctx->segments + 1;
max_length = ptex->length;
for (k = 0, key = keys; k < totkeys; k++, key++) {
psys_path_iter_get(&iter, keys, totkeys, parent_keys, k);
/* apply different deformations to the child path */
do_child_modifiers(&ctx->sim, ptex, (ParticleKey *)iter.parent_key, iter.parent_rotation, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, iter.time, spiral_start, &time_prev, co_prev);
do_child_modifiers(&ctx->sim, ptex, (ParticleKey *)iter.parent_key, iter.parent_rotation, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, iter.time);
}
}
{
const float step_length = 1.0f / (float)(totkeys - 1);
float cur_length = 0.0f;
/* we have to correct velocity because of kink & clump */
for (k = 0, key = keys; k < totkeys; ++k, ++key) {
if (k >= 2) {
sub_v3_v3v3((key-1)->vel, key->co, (key-2)->co);
mul_v3_fl((key-1)->vel, 0.5);
if (ma && draw_col_ma)
get_strand_normal(ma, ornor, cur_length, (key-1)->vel);
}
else if (k == totkeys-1) {
/* last key */
sub_v3_v3v3(key->vel, key->co, (key-1)->co);
}
if (k > 1) {
float dvec[3];
/* check if path needs to be cut before actual end of data points */
if (!check_path_length(k, keys, key, max_length, step_length, &cur_length, dvec))
break;
}
if (ma && draw_col_ma) {
copy_v3_v3(key->col, &ma->r);
get_strand_normal(ma, ornor, cur_length, key->vel);
}
}
}
#endif
cur_length = 0.0f;
/* we have to correct velocity because of kink & clump */
for (k = 0, key = keys; k < totkeys; ++k, ++key) {
if (k >= 2) {
sub_v3_v3v3((key-1)->vel, key->co, (key-2)->co);
mul_v3_fl((key-1)->vel, 0.5);
if (ma && draw_col_ma)
get_strand_normal(ma, ornor, cur_length, (key-1)->vel);
}
else if (k == totkeys-1) {
/* last key */
sub_v3_v3v3(key->vel, key->co, (key-1)->co);
}
if (k > 1) {
float dvec[3];
/* check if path needs to be cut before actual end of data points */
if (!check_path_length(k, keys, key, max_length, step_length, &cur_length, dvec))
break;
}
if (ma && draw_col_ma) {
copy_v3_v3(key->col, &ma->r);
get_strand_normal(ma, ornor, cur_length, key->vel);
}
}
}
/* ------------------------------------------------------------------------- */
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 spiral_start[3], float *time_prev, float *co_prev)
float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start)
{
float kink[3] = {1.f, 0.f, 0.f}, par_vec[3], q1[4] = {1.f, 0.f, 0.f, 0.f};
float t, dt = 1.f, result[3];
if (par == NULL || type == PART_KINK_NO)
if (par == NULL || ELEM(type, PART_KINK_NO, PART_KINK_SPIRAL))
return;
CLAMP(time, 0.f, 1.f);
if (shape != 0.0f && !ELEM(type, PART_KINK_BRAID, PART_KINK_SPIRAL)) {
if (shape != 0.0f && !ELEM(type, PART_KINK_BRAID)) {
if (shape < 0.0f)
time = (float)pow(time, 1.f + shape);
else
@ -228,14 +390,14 @@ void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, f
t = time * freq * (float)M_PI;
if (smooth_start && !ELEM(type, PART_KINK_SPIRAL)) {
if (smooth_start) {
dt = fabsf(t);
/* smooth the beginning of kink */
CLAMP(dt, 0.f, (float)M_PI);
dt = sinf(dt / 2.f);
}
if (!ELEM(type, PART_KINK_RADIAL, PART_KINK_SPIRAL)) {
if (!ELEM(type, PART_KINK_RADIAL)) {
float temp[3];
kink[axis] = 1.f;
@ -359,65 +521,6 @@ void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, f
}
break;
}
case PART_KINK_SPIRAL:
{
if (time <= flat) {
/* nothing to do for the flat section */
}
else {
#if 0
/* Creates a logarithmic spiral:
* r(theta) = a * exp(b * theta)
*
* For now chose the golden spiral for the "density" parameter b:
* http://en.wikipedia.org/wiki/Golden_spiral
* This could be configurable, but the golden spiral is quite pleasant and natural
*/
const float b = (1.0f + sqrtf(5.0f)) / (2.0f * M_PI);
const float arc_factor = sqrtf(1.0f + b*b) / b;
/* Relation to amplitude (spiral radius):
* a*exp(b*theta0) = sqrt(2) * R
*
* Arc length of the logarithmic spiral:
* s(theta) = a*exp(b*theta) * (1 + b^2)/b
*/
const float a = arc_length / (sqrtf(2.0f) * amplitude * arc_factor);
const float theta0 = logf(sqrtf(2.0f) * amplitude / a) / b;
float arc = max_length - time;
float theta = logf(arc / (a * arc_factor)) / b;
#else
// float theta = (time - flat) / amplitude;
#endif
float dir[3], up[3], rot[3][3];
float vec[3];
float theta = freq * (time - flat) / (1.0f - flat);
// float arc = theta * amplitude;
float radius = amplitude * expf(shape * theta);
normalize_v3_v3(dir, par->vel);
cross_v3_v3v3(up, dir, kink);
axis_angle_normalized_to_mat3(rot, kink, theta);
if (*time_prev <= flat) {
interp_v3_v3v3(spiral_start, co_prev, state->co, (flat - *time_prev) / (time - *time_prev));
}
mul_v3_v3fl(vec, up, radius);
mul_v3_m3v3(result, rot, vec);
madd_v3_v3fl(result, up, -radius);
add_v3_v3(result, spiral_start);
}
*time_prev = time;
copy_v3_v3(co_prev, state->co);
break;
}
}
/* blend the start of the kink */
@ -540,7 +643,7 @@ static void do_rough_curve(const float loc[3], float mat[4][4], float time, floa
}
void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, ParticleKey *par, float *par_rot, const float par_orco[3],
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t, float spiral_start[3], float *time_prev, float *co_prev)
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t)
{
ParticleSettings *part = sim->psys->part;
int i = cpa - sim->psys->child;
@ -550,6 +653,7 @@ void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, Part
float rough1 = part->rough1;
float rough2 = part->rough2;
float rough_end = part->rough_end;
const bool smooth_start = (sim->psys->part->childtype == PART_CHILD_FACES);
if (ptex) {
kink_freq *= ptex->kink;
@ -580,8 +684,7 @@ void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, Part
do_kink(state, par, par_rot, t, kink_freq, part->kink_shape,
kink_amp, part->kink_flat, part->kink, part->kink_axis,
sim->ob->obmat, sim->psys->part->childtype == PART_CHILD_FACES,
spiral_start, time_prev, co_prev);
sim->ob->obmat, smooth_start);
}
}

@ -498,4 +498,11 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
part->clump_noise_size = 1.0f;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
part->kink_extra_steps = 4;
}
}
}

@ -440,9 +440,9 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *
for (a = 0; a < totpart; a++) {
key = cache[a];
if (key->steps > 0) {
totvert += key->steps + 1;
totedge += key->steps;
if (key->segments > 0) {
totvert += key->segments + 1;
totedge += key->segments;
}
}
@ -450,9 +450,9 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *
for (a = 0; a < totchild; a++) {
key = cache[a];
if (key->steps > 0) {
totvert += key->steps + 1;
totedge += key->steps;
if (key->segments > 0) {
totvert += key->segments + 1;
totedge += key->segments;
}
}
@ -476,7 +476,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *
cache = psys->pathcache;
for (a = 0; a < totpart; a++) {
key = cache[a];
kmax = key->steps;
kmax = key->segments;
for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) {
copy_v3_v3(mvert->co, key->co);
if (k) {
@ -495,7 +495,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *
cache = psys->childcache;
for (a = 0; a < totchild; a++) {
key = cache[a];
kmax = key->steps;
kmax = key->segments;
for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) {
copy_v3_v3(mvert->co, key->co);
if (k) {

@ -5144,7 +5144,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
cache = psys->pathcache;
for (a = 0, pa = psys->particles; a < totpart; a++, pa++) {
path = cache[a];
if (path->steps > 0) {
if (path->segments > 0) {
glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
if (1) { //ob_dt > OB_WIRE) {
@ -5156,7 +5156,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
}
}
glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1);
glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
}
}
@ -5304,7 +5304,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
}
}
glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1);
glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
}
@ -5456,7 +5456,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
PTCacheEditKey *key;
ParticleEditSettings *pset = PE_settings(scene);
int i, k, totpoint = edit->totpoint, timed = pset->flag & PE_FADE_TIME ? pset->fade_frames : 0;
int steps = 1;
int totkeys = 1;
float sel_col[3];
float nosel_col[3];
float *pathcol = NULL, *pcol;
@ -5475,10 +5475,10 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
UI_GetThemeColor3fv(TH_VERTEX, nosel_col);
/* draw paths */
steps = (*edit->pathcache)->steps + 1;
totkeys = (*edit->pathcache)->segments + 1;
glEnable(GL_BLEND);
pathcol = MEM_callocN(steps * 4 * sizeof(float), "particle path color data");
pathcol = MEM_callocN(totkeys * 4 * sizeof(float), "particle path color data");
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
@ -5498,7 +5498,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
if (point->flag & PEP_HIDE) {
for (k = 0, pcol = pathcol; k < steps; k++, pcol += 4) {
for (k = 0, pcol = pathcol; k < totkeys; k++, pcol += 4) {
copy_v3_v3(pcol, path->col);
pcol[3] = 0.25f;
}
@ -5506,7 +5506,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
glColorPointer(4, GL_FLOAT, 4 * sizeof(float), pathcol);
}
else if (timed) {
for (k = 0, pcol = pathcol, pkey = path; k < steps; k++, pkey++, pcol += 4) {
for (k = 0, pcol = pathcol, pkey = path; k < totkeys; k++, pkey++, pcol += 4) {
copy_v3_v3(pcol, pkey->col);
pcol[3] = 1.0f - fabsf((float)(CFRA) -pkey->time) / (float)pset->fade_frames;
}
@ -5516,7 +5516,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
else
glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1);
glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
}
if (pathcol) { MEM_freeN(pathcol); pathcol = pcol = NULL; }

@ -218,6 +218,7 @@ typedef struct ParticleSettings {
/* kink */
float kink_amp, kink_freq, kink_shape, kink_flat;
float kink_amp_clump;
int kink_extra_steps, pad;
/* rough */
float rough1, rough1_size;
float rough2, rough2_size, rough2_thres;

@ -363,7 +363,7 @@ static void rna_ParticleSystem_co_hair(ParticleSystem *particlesystem, Object *o
if (path_nbr) {
cache = particlesystem->pathcache[particle_no];
max_k = (int)cache->steps;
max_k = (int)cache->segments;
}
}
@ -372,10 +372,10 @@ static void rna_ParticleSystem_co_hair(ParticleSystem *particlesystem, Object *o
if (path_nbr) {
cache = particlesystem->childcache[particle_no - totpart];
if (cache->steps < 0)
if (cache->segments < 0)
max_k = 0;
else
max_k = (int)cache->steps;
max_k = (int)cache->segments;
}
}
@ -2900,6 +2900,12 @@ static void rna_def_particle_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Flatness", "How flat the hairs are");
RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
prop = RNA_def_property(srna, "kink_extra_steps", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 1, INT_MAX);
RNA_def_property_ui_range(prop, 1, 100, 1, -1);
RNA_def_property_ui_text(prop, "Extra Steps", "Extra steps for resolution of special kink features");
RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
/* rough */
prop = RNA_def_property(srna, "roughness_1", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rough1");

@ -1551,7 +1551,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
if (path_nbr) {
cache = psys->pathcache[a];
max_k = (int)cache->steps;
max_k = (int)cache->segments;
}
if (totchild && (part->draw&PART_DRAW_PARENT)==0) continue;
@ -1562,10 +1562,10 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
if (path_nbr) {
cache = psys->childcache[a-totpart];
if (cache->steps < 0)
if (cache->segments < 0)
continue;
max_k = (int)cache->steps;
max_k = (int)cache->segments;
}
pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);

@ -160,7 +160,7 @@ static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, Pa
else
continue;
cache += cache->steps; /* use endpoint */
cache += cache->segments; /* use endpoint */
copy_v3_v3(state.co, cache->co);
zero_v3(state.vel);