diff options
Diffstat (limited to 'source/blender/blenkernel/intern/particle_system.c')
-rw-r--r-- | source/blender/blenkernel/intern/particle_system.c | 1402 |
1 files changed, 663 insertions, 739 deletions
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 336b683e26b..56291ad753c 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -329,6 +329,10 @@ static void alloc_child_particles(ParticleSystem *psys, int tot) } } +/************************************************/ +/* Distribution */ +/************************************************/ + void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys) { /* use for building derived mesh mapping info: @@ -405,7 +409,36 @@ void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys) } } -static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys) +static void distribute_simple_children(Scene *scene, Object *ob, DerivedMesh *finaldm, ParticleSystem *psys) +{ + ChildParticle *cpa = NULL; + int i, p; + int child_nbr= get_psys_child_number(scene, psys); + int totpart= get_psys_tot_child(scene, psys); + + alloc_child_particles(psys, totpart); + + cpa = psys->child; + for(i=0; i<child_nbr; i++){ + for(p=0; p<psys->totpart; p++,cpa++){ + float length=2.0; + cpa->parent=p; + + /* create even spherical distribution inside unit sphere */ + while(length>=1.0f){ + cpa->fuv[0]=2.0f*BLI_frand()-1.0f; + cpa->fuv[1]=2.0f*BLI_frand()-1.0f; + cpa->fuv[2]=2.0f*BLI_frand()-1.0f; + length=len_v3(cpa->fuv); + } + + cpa->num=-1; + } + } + /* dmcache must be updated for parent particles if children from faces is used */ + psys_calc_dmcache(ob, finaldm, psys); +} +static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys) { ParticleData *pa=NULL; float min[3], max[3], delta[3], d; @@ -686,7 +719,7 @@ static void psys_uv_to_w(float u, float v, int quad, float *w) } /* Find the index in "sum" array before "value" is crossed. */ -static int binary_search_distribution(float *sum, int n, float value) +static int distribute_binary_search(float *sum, int n, float value) { int mid, low=0, high=n; @@ -716,13 +749,11 @@ static int binary_search_distribution(float *sum, int n, float value) /* note: this function must be thread safe, for from == PART_FROM_CHILD */ #define ONLY_WORKING_WITH_PA_VERTS 0 -static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p) +static void distribute_threads_exec(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p) { ParticleThreadContext *ctx= thread->ctx; Object *ob= ctx->sim.ob; DerivedMesh *dm= ctx->dm; - ParticleData *tpa; -/* ParticleSettings *part= ctx->sim.psys->part; */ float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3]; float cur_d, min_d, randu, randv; int from= ctx->from; @@ -760,9 +791,17 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData switch(distr){ case PART_DISTR_JIT: - ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); - psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); - ctx->jitoff[i]++; + if(ctx->jitlevel == 1) { + if(mface->v4) + psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv); + else + psys_uv_to_w(0.33333f, 0.33333f, mface->v4, pa->fuv); + } + else { + ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); + psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); + ctx->jitoff[i]++; + } break; case PART_DISTR_RAND: randu= rng_getFloat(thread->rng); @@ -828,15 +867,6 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData } } } - else if(from == PART_FROM_PARTICLE) { - tpa=ctx->tpars+ctx->index[p]; - pa->num=ctx->index[p]; - pa->fuv[0]=tpa->fuv[0]; - pa->fuv[1]=tpa->fuv[1]; - /* abusing foffset a little for timing in near reaction */ - pa->foffset=ctx->weight[ctx->index[p]]; - ctx->weight[ctx->index[p]]+=ctx->maxweight; - } else if(from == PART_FROM_CHILD) { MFace *mf; @@ -870,7 +900,6 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData maxd=ptn[maxw-1].dist; mind=ptn[0].dist; - /*dd=maxd-mind;*/ /*UNUSED*/ /* the weights here could be done better */ for(w=0; w<maxw; w++){ @@ -906,7 +935,7 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData rng_skip(thread->rng, rng_skip_tot); } -static void *exec_distribution(void *data) +static void *distribute_threads_exec_cb(void *data) { ParticleThread *thread= (ParticleThread*)data; ParticleSystem *psys= thread->ctx->sim.psys; @@ -923,7 +952,7 @@ static void *exec_distribution(void *data) rng_skip(thread->rng, PSYS_RND_DIST_SKIP * thread->ctx->skip[p]); if((p+thread->num) % thread->tot == 0) - psys_thread_distribute_particle(thread, NULL, cpa, p); + distribute_threads_exec(thread, NULL, cpa, p); else /* thread skip */ rng_skip(thread->rng, PSYS_RND_DIST_SKIP); } @@ -932,7 +961,7 @@ static void *exec_distribution(void *data) totpart= psys->totpart; pa= psys->particles + thread->num; for(p=thread->num; p<totpart; p+=thread->tot, pa+=thread->tot) - psys_thread_distribute_particle(thread, pa, NULL, p); + distribute_threads_exec(thread, pa, NULL, p); } return 0; @@ -940,7 +969,7 @@ static void *exec_distribution(void *data) /* not thread safe, but qsort doesn't take userdata argument */ static int *COMPARE_ORIG_INDEX = NULL; -static int compare_orig_index(const void *p1, const void *p2) +static int distribute_compare_orig_index(const void *p1, const void *p2) { int index1 = COMPARE_ORIG_INDEX[*(const int*)p1]; int index2 = COMPARE_ORIG_INDEX[*(const int*)p2]; @@ -961,44 +990,53 @@ static int compare_orig_index(const void *p1, const void *p2) return 1; } -/* creates a distribution of coordinates on a DerivedMesh */ -/* */ -/* 1. lets check from what we are emitting */ -/* 2. now we know that we have something to emit from so */ -/* let's calculate some weights */ -/* 2.1 from even distribution */ -/* 2.2 and from vertex groups */ -/* 3. next we determine the indexes of emitting thing that */ -/* the particles will have */ -/* 4. let's do jitter if we need it */ -/* 5. now we're ready to set the indexes & distributions to */ -/* the particles */ -/* 6. and we're done! */ +static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from) +{ + if(from == PART_FROM_CHILD) { + ChildParticle *cpa; + int p, totchild = get_psys_tot_child(scene, psys); + + if(psys->child && totchild) { + for(p=0,cpa=psys->child; p<totchild; p++,cpa++){ + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0; + cpa->foffset= 0.0f; + cpa->parent=0; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + cpa->num= -1; + } + } + } + else { + PARTICLE_P; + LOOP_PARTICLES { + pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0; + pa->foffset= 0.0f; + pa->num= -1; + } + } +} +/* Creates a distribution of coordinates on a DerivedMesh */ /* This is to denote functionality that does not yet work with mesh - only derived mesh */ -static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from) +static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from) { ParticleThreadContext *ctx= threads[0].ctx; Object *ob= ctx->sim.ob; ParticleSystem *psys= ctx->sim.psys; - Object *tob; ParticleData *pa=0, *tpars= 0; ParticleSettings *part; - ParticleSystem *tpsys; ParticleSeam *seams= 0; - ChildParticle *cpa=0; KDTree *tree=0; DerivedMesh *dm= NULL; float *jit= NULL; int i, seed, p=0, totthread= threads[0].tot; - int /*no_distr=0,*/ cfrom=0; - int tot=0, totpart, *index=0, children=0, totseam=0; - //int *vertpart=0; + int cfrom=0; + int totelem=0, totpart, *particle_element=0, children=0, totseam=0; int jitlevel= 1, distr; - float *weight=0,*sum=0,*jitoff=0; - float cur, maxweight=0.0, tweight, totweight, co[3], nor[3], orco[3], ornor[3]; + float *element_weight=NULL,*element_sum=NULL,*jitter_offset=NULL, *vweight=NULL; + float cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3], ornor[3]; - if(ob==0 || psys==0 || psys->part==0) + if(ELEM3(NULL, ob, psys, psys->part)) return 0; part=psys->part; @@ -1012,81 +1050,63 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, return 0; } - BLI_srandom(31415926 + psys->seed); + /* First handle special cases */ + if(from == PART_FROM_CHILD) { + /* Simple children */ + if(part->childtype != PART_CHILD_FACES) { + BLI_srandom(31415926 + psys->seed + psys->child_seed); + distribute_simple_children(scene, ob, finaldm, psys); + return 0; + } + } + else { + /* Grid distribution */ + if(part->distr==PART_DISTR_GRID && from != PART_FROM_VERT){ + BLI_srandom(31415926 + psys->seed); + dm= CDDM_from_mesh((Mesh*)ob->data, ob); + distribute_grid(dm,psys); + dm->release(dm); + return 0; + } + } - if(from==PART_FROM_CHILD){ + /* Create trees and original coordinates if needed */ + if(from == PART_FROM_CHILD) { distr=PART_DISTR_RAND; BLI_srandom(31415926 + psys->seed + psys->child_seed); + dm= finaldm; + children=1; - if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ - dm= finaldm; - children=1; - - tree=BLI_kdtree_new(totpart); + tree=BLI_kdtree_new(totpart); - for(p=0,pa=psys->particles; p<totpart; p++,pa++){ - psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,ornor); - transform_mesh_orco_verts((Mesh*)ob->data, &orco, 1, 1); - BLI_kdtree_insert(tree, p, orco, ornor); - } - - BLI_kdtree_balance(tree); - - totpart=get_psys_tot_child(scene, psys); - cfrom=from=PART_FROM_FACE; + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,ornor); + transform_mesh_orco_verts((Mesh*)ob->data, &orco, 1, 1); + BLI_kdtree_insert(tree, p, orco, ornor); } - else{ - /* no need to figure out distribution */ - int child_nbr= get_psys_child_number(scene, psys); - - totpart= get_psys_tot_child(scene, psys); - alloc_child_particles(psys, totpart); - cpa=psys->child; - for(i=0; i<child_nbr; i++){ - for(p=0; p<psys->totpart; p++,cpa++){ - float length=2.0; - cpa->parent=p; - - /* create even spherical distribution inside unit sphere */ - while(length>=1.0f){ - cpa->fuv[0]=2.0f*BLI_frand()-1.0f; - cpa->fuv[1]=2.0f*BLI_frand()-1.0f; - cpa->fuv[2]=2.0f*BLI_frand()-1.0f; - length=len_v3(cpa->fuv); - } - cpa->num=-1; - } - } - /* dmcache must be updated for parent particles if children from faces is used */ - psys_calc_dmcache(ob, finaldm, psys); + BLI_kdtree_balance(tree); - return 0; - } + totpart = get_psys_tot_child(scene, psys); + cfrom = from = PART_FROM_FACE; } - else{ + else { + distr = part->distr; + BLI_srandom(31415926 + psys->seed); + dm= CDDM_from_mesh((Mesh*)ob->data, ob); - /* special handling of grid distribution */ - if(part->distr==PART_DISTR_GRID && from != PART_FROM_VERT){ - distribute_particles_in_grid(dm,psys); - dm->release(dm); - return 0; - } - /* we need orco for consistent distributions */ DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, get_mesh_orco_verts(ob)); - distr=part->distr; - - if(from==PART_FROM_VERT){ + if(from == PART_FROM_VERT) { MVert *mv= dm->getVertDataArray(dm, CD_MVERT); float (*orcodata)[3]= dm->getVertDataArray(dm, CD_ORCO); int totvert = dm->getNumVerts(dm); tree=BLI_kdtree_new(totvert); - for(p=0; p<totvert; p++){ + for(p=0; p<totvert; p++) { if(orcodata) { VECCOPY(co,orcodata[p]) transform_mesh_orco_verts((Mesh*)ob->data, &co, 1, 1); @@ -1100,73 +1120,33 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, } } - /* 1. */ - switch(from){ - case PART_FROM_VERT: - tot = dm->getNumVerts(dm); - break; - case PART_FROM_VOLUME: - case PART_FROM_FACE: - tot = dm->getNumFaces(dm); - break; - case PART_FROM_PARTICLE: - if(psys->target_ob) - tob=psys->target_ob; - else - tob=ob; + /* Get total number of emission elements and allocate needed arrays */ + totelem = (from == PART_FROM_VERT) ? dm->getNumVerts(dm) : dm->getNumFaces(dm); - if((tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1))){ - tpars=tpsys->particles; - tot=tpsys->totpart; - } - break; - } + if(totelem == 0){ + distribute_invalid(scene, psys, children ? PART_FROM_CHILD : 0); - if(tot==0){ - /*no_distr=1;*/ /*UNUSED*/ - if(children){ - if(G.f & G_DEBUG) - fprintf(stderr,"Particle child distribution error: Nothing to emit from!\n"); - if(psys->child) { - for(p=0,cpa=psys->child; p<totpart; p++,cpa++){ - cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0; - cpa->foffset= 0.0f; - cpa->parent=0; - cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; - cpa->num= -1; - } - } - } - else { - if(G.f & G_DEBUG) - fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); - for(p=0,pa=psys->particles; p<totpart; p++,pa++){ - pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0; - pa->foffset= 0.0f; - pa->num= -1; - } - } + if(G.f & G_DEBUG) + fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); if(dm != finaldm) dm->release(dm); return 0; } - /* 2. */ - - weight=MEM_callocN(sizeof(float)*tot, "particle_distribution_weights"); - index=MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); - sum=MEM_callocN(sizeof(float)*(tot+1), "particle_distribution_sum"); - jitoff=MEM_callocN(sizeof(float)*tot, "particle_distribution_jitoff"); + element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights"); + particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); + element_sum = MEM_callocN(sizeof(float)*(totelem+1), "particle_distribution_sum"); + jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff"); - /* 2.1 */ - if((part->flag&PART_EDISTR || children) && ELEM(from,PART_FROM_PARTICLE,PART_FROM_VERT)==0){ + /* Calculate weights from face areas */ + if((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT){ MVert *v1, *v2, *v3, *v4; - float totarea=0.0, co1[3], co2[3], co3[3], co4[3]; + float totarea=0.f, co1[3], co2[3], co3[3], co4[3]; float (*orcodata)[3]; orcodata= dm->getVertDataArray(dm, CD_ORCO); - for(i=0; i<tot; i++){ + for(i=0; i<totelem; i++){ MFace *mf=dm->getFaceData(dm,i,CD_MFACE); if(orcodata) { @@ -1176,6 +1156,10 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, transform_mesh_orco_verts((Mesh*)ob->data, &co1, 1, 1); transform_mesh_orco_verts((Mesh*)ob->data, &co2, 1, 1); transform_mesh_orco_verts((Mesh*)ob->data, &co3, 1, 1); + if(mf->v4) { + VECCOPY(co4, orcodata[mf->v4]); + transform_mesh_orco_verts((Mesh*)ob->data, &co4, 1, 1); + } } else { v1= (MVert*)dm->getVertData(dm,mf->v1,CD_MVERT); @@ -1184,156 +1168,133 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, VECCOPY(co1, v1->co); VECCOPY(co2, v2->co); VECCOPY(co3, v3->co); - } - - if (mf->v4){ - if(orcodata) { - VECCOPY(co4, orcodata[mf->v4]); - transform_mesh_orco_verts((Mesh*)ob->data, &co4, 1, 1); - } - else { + if(mf->v4) { v4= (MVert*)dm->getVertData(dm,mf->v4,CD_MVERT); VECCOPY(co4, v4->co); } - cur= area_quad_v3(co1, co2, co3, co4); } - else - cur= area_tri_v3(co1, co2, co3); + + cur = mf->v4 ? area_quad_v3(co1, co2, co3, co4) : area_tri_v3(co1, co2, co3); - if(cur>maxweight) - maxweight=cur; + if(cur > maxweight) + maxweight = cur; - weight[i]= cur; - totarea+=cur; + element_weight[i] = cur; + totarea += cur; } - for(i=0; i<tot; i++) - weight[i] /= totarea; + for(i=0; i<totelem; i++) + element_weight[i] /= totarea; maxweight /= totarea; } - else if(from==PART_FROM_PARTICLE){ - float val=(float)tot/(float)totpart; - for(i=0; i<tot; i++) - weight[i]=val; - maxweight=val; - } else{ - float min=1.0f/(float)(MIN2(tot,totpart)); - for(i=0; i<tot; i++) - weight[i]=min; + float min=1.0f/(float)(MIN2(totelem,totpart)); + for(i=0; i<totelem; i++) + element_weight[i]=min; maxweight=min; } - /* 2.2 */ - if(ELEM3(from,PART_FROM_VERT,PART_FROM_FACE,PART_FROM_VOLUME)){ - float *vweight= psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY); + /* Calculate weights from vgroup */ + vweight = psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY); - if(vweight){ - if(from==PART_FROM_VERT) { - for(i=0;i<tot; i++) - weight[i]*=vweight[i]; - } - else { /* PART_FROM_FACE / PART_FROM_VOLUME */ - for(i=0;i<tot; i++){ - MFace *mf=dm->getFaceData(dm,i,CD_MFACE); - tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3]; + if(vweight){ + if(from==PART_FROM_VERT) { + for(i=0;i<totelem; i++) + element_weight[i]*=vweight[i]; + } + else { /* PART_FROM_FACE / PART_FROM_VOLUME */ + for(i=0;i<totelem; i++){ + MFace *mf=dm->getFaceData(dm,i,CD_MFACE); + tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3]; - if(mf->v4) { - tweight += vweight[mf->v4]; - tweight /= 4.0; - } - else { - tweight /= 3.0; - } - - weight[i]*=tweight; + if(mf->v4) { + tweight += vweight[mf->v4]; + tweight /= 4.0; } + else { + tweight /= 3.0; + } + + element_weight[i]*=tweight; } - MEM_freeN(vweight); } + MEM_freeN(vweight); } - /* 3. */ + /* Calculate total weight of all elements */ totweight= 0.0f; - for(i=0;i<tot; i++) - totweight += weight[i]; + for(i=0;i<totelem; i++) + totweight += element_weight[i]; - if(totweight > 0.0f) - totweight= 1.0f/totweight; + inv_totweight = (totweight > 0.f ? 1.f/totweight : 0.f); - sum[0]= 0.0f; - for(i=0;i<tot; i++) - sum[i+1]= sum[i]+weight[i]*totweight; + /* Calculate cumulative weights */ + element_sum[0]= 0.0f; + for(i=0; i<totelem; i++) + element_sum[i+1]= element_sum[i] + element_weight[i] * inv_totweight; + /* Finally assign elements to particles */ if((part->flag&PART_TRAND) || (part->simplify_flag&PART_SIMPLIFY_ENABLE)) { float pos; for(p=0; p<totpart; p++) { - /* In theory sys[tot] should be 1.0, but due to float errors this is not necessarily always true, so scale pos accordingly. */ - pos= BLI_frand() * sum[tot]; - index[p]= binary_search_distribution(sum, tot, pos); - index[p]= MIN2(tot-1, index[p]); - jitoff[index[p]]= pos; + /* In theory element_sum[totelem] should be 1.0, but due to float errors this is not necessarily always true, so scale pos accordingly. */ + pos= BLI_frand() * element_sum[totelem]; + particle_element[p]= distribute_binary_search(element_sum, totelem, pos); + particle_element[p]= MIN2(totelem-1, particle_element[p]); + jitter_offset[particle_element[p]]= pos; } } else { double step, pos; - step= (totpart <= 1)? 0.5: 1.0/(totpart-1); - pos= 1e-16f; /* tiny offset to avoid zero weight face */ + step= (totpart < 2) ? 0.5 : 1.0/(double)totpart; + pos= 1e-16; /* tiny offset to avoid zero weight face */ i= 0; for(p=0; p<totpart; p++, pos+=step) { - while((i < tot) && (pos > sum[i+1])) + while((i < totelem) && (pos > element_sum[i+1])) i++; - index[p]= MIN2(tot-1, i); + particle_element[p]= MIN2(totelem-1, i); /* avoid zero weight face */ - if(p == totpart-1 && weight[index[p]] == 0.0f) - index[p]= index[p-1]; + if(p == totpart-1 && element_weight[particle_element[p]] == 0.0f) + particle_element[p]= particle_element[p-1]; - jitoff[index[p]]= pos; + jitter_offset[particle_element[p]]= pos; } } - MEM_freeN(sum); + MEM_freeN(element_sum); - /* for hair, sort by origindex, allows optimizations in rendering */ - /* however with virtual parents the children need to be in random order */ + /* For hair, sort by origindex (allows optimizations in rendering), */ + /* however with virtual parents the children need to be in random order. */ if(part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0)) { - if(from != PART_FROM_PARTICLE) { - COMPARE_ORIG_INDEX = NULL; - - if(from == PART_FROM_VERT) { - if(dm->numVertData) - COMPARE_ORIG_INDEX= dm->getVertDataArray(dm, CD_ORIGINDEX); - } - else { - if(dm->numFaceData) - COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX); - } + COMPARE_ORIG_INDEX = NULL; - if(COMPARE_ORIG_INDEX) { - qsort(index, totpart, sizeof(int), compare_orig_index); - COMPARE_ORIG_INDEX = NULL; - } + if(from == PART_FROM_VERT) { + if(dm->numVertData) + COMPARE_ORIG_INDEX= dm->getVertDataArray(dm, CD_ORIGINDEX); + } + else { + if(dm->numFaceData) + COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX); } - } - /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */ - if(from==PART_FROM_PARTICLE){ - for(i=0; i<tot; i++) - weight[i]=0.0f; + if(COMPARE_ORIG_INDEX) { + qsort(particle_element, totpart, sizeof(int), distribute_compare_orig_index); + COMPARE_ORIG_INDEX = NULL; + } } - /* 4. */ + /* Create jittering if needed */ if(distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { jitlevel= part->userjit; if(jitlevel == 0) { - jitlevel= totpart/tot; + jitlevel= totpart/totelem; if(part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */ if(jitlevel<3) jitlevel= 3; } @@ -1350,16 +1311,16 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */ } - /* 5. */ + /* Setup things for threaded distribution */ ctx->tree= tree; ctx->seams= seams; ctx->totseam= totseam; ctx->sim.psys= psys; - ctx->index= index; + ctx->index= particle_element; ctx->jit= jit; ctx->jitlevel= jitlevel; - ctx->jitoff= jitoff; - ctx->weight= weight; + ctx->jitoff= jitter_offset; + ctx->weight= element_weight; ctx->maxweight= maxweight; ctx->from= (children)? PART_FROM_CHILD: from; ctx->cfrom= cfrom; @@ -1394,14 +1355,14 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) pthreads= psys_threads_create(sim); - if(!psys_threads_init_distribution(pthreads, sim->scene, finaldm, from)) { + if(!distribute_threads_init_data(pthreads, sim->scene, finaldm, from)) { psys_threads_free(pthreads); return; } totthread= pthreads[0].tot; if(totthread > 1) { - BLI_init_threads(&threads, exec_distribution, totthread); + BLI_init_threads(&threads, distribute_threads_exec_cb, totthread); for(i=0; i<totthread; i++) BLI_insert_thread(&threads, &pthreads[i]); @@ -1409,7 +1370,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) BLI_end_threads(&threads); } else - exec_distribution(&pthreads[0]); + distribute_threads_exec_cb(&pthreads[0]); psys_calc_dmcache(sim->ob, finaldm, sim->psys); @@ -1423,17 +1384,11 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) /* ready for future use, to emit particles without geometry */ static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSED(from)) { - ParticleSystem *psys = sim->psys; - PARTICLE_P; + distribute_invalid(sim->scene, sim->psys, 0); fprintf(stderr,"Shape emission not yet possible!\n"); - - LOOP_PARTICLES { - pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0; - pa->foffset= 0.0f; - pa->num= -1; - } } + static void distribute_particles(ParticleSimulationData *sim, int from) { PARTICLE_PSMD; @@ -1449,16 +1404,9 @@ static void distribute_particles(ParticleSimulationData *sim, int from) distribute_particles_on_shape(sim, from); if(distr_error){ - ParticleSystem *psys = sim->psys; - PARTICLE_P; + distribute_invalid(sim->scene, sim->psys, from); fprintf(stderr,"Particle distribution error!\n"); - - LOOP_PARTICLES { - pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0; - pa->foffset= 0.0f; - pa->num= -1; - } } } @@ -1547,7 +1495,7 @@ void initialize_particle(ParticleSimulationData *sim, ParticleData *pa, int p) pa->flag &= ~PARS_UNEXIST; - if(part->from != PART_FROM_PARTICLE && part->type != PART_FLUID) { + if(part->type != PART_FLUID) { psys_get_texture(sim, pa, &ptex, PAMAP_INIT, 0.f); if(ptex.exist < PSYS_FRAND(p+125)) @@ -1628,34 +1576,6 @@ void reset_particle(ParticleSimulationData *sim, ParticleData *pa, float dtime, int p = pa - psys->particles; part=psys->part; -#if 0 /* deprecated code */ - if(part->from==PART_FROM_PARTICLE){ - float speed; - ParticleSimulationData tsim= {0}; - tsim.scene= sim->scene; - tsim.ob= psys->target_ob ? psys->target_ob : ob; - tsim.psys = BLI_findlink(&tsim.ob->particlesystem, sim->psys->target_psys-1); - - state.time = pa->time; - if(pa->num == -1) - memset(&state, 0, sizeof(state)); - else - psys_get_particle_state(&tsim, pa->num, &state, 1); - psys_get_from_key(&state, loc, nor, rot, 0); - - mul_qt_v3(rot, vtan); - mul_qt_v3(rot, utan); - - speed= normalize_v3_v3(p_vel, state.vel); - mul_v3_fl(p_vel, dot_v3v3(r_vel, p_vel)); - VECSUB(p_vel, r_vel, p_vel); - normalize_v3(p_vel); - mul_v3_fl(p_vel, speed); - - VECCOPY(pa->fuv, loc); /* abusing pa->fuv (not used for "from particle") for storing emit location */ - } - else{ -#endif /* get precise emitter matrix if particle is born */ if(part->type!=PART_HAIR && pa->time < cfra && pa->time >= sim->psys->cfra) { /* we have to force RECALC_ANIM here since where_is_objec_time only does drivers */ @@ -2037,112 +1957,7 @@ static void set_keyed_keys(ParticleSimulationData *sim) psys->flag |= PSYS_KEYED; } -/************************************************/ -/* Reactors */ -/************************************************/ -//static void push_reaction(ParticleSimulationData *sim, int pa_num, int event, ParticleKey *state) -//{ -// Object *rob; -// ParticleSystem *rpsys; -// ParticleSettings *rpart; -// ParticleData *pa; -// ListBase *lb=&sim->psys->effectors; -// ParticleEffectorCache *ec; -// ParticleReactEvent *re; -// -// if(lb->first) for(ec = lb->first; ec; ec= ec->next){ -// if(ec->type & PSYS_EC_REACTOR){ -// /* all validity checks already done in add_to_effectors */ -// rob=ec->ob; -// rpsys=BLI_findlink(&rob->particlesystem,ec->psys_nbr); -// rpart=rpsys->part; -// if(rpsys->part->reactevent==event){ -// pa=sim->psys->particles+pa_num; -// re= MEM_callocN(sizeof(ParticleReactEvent), "react event"); -// re->event=event; -// re->pa_num = pa_num; -// re->ob = sim->ob; -// re->psys = sim->psys; -// re->size = pa->size; -// copy_particle_key(&re->state,state,1); -// -// switch(event){ -// case PART_EVENT_DEATH: -// re->time=pa->dietime; -// break; -// case PART_EVENT_COLLIDE: -// re->time=state->time; -// break; -// case PART_EVENT_NEAR: -// re->time=state->time; -// break; -// } -// -// BLI_addtail(&rpsys->reactevents, re); -// } -// } -// } -//} -//static void react_to_events(ParticleSystem *psys, int pa_num) -//{ -// ParticleSettings *part=psys->part; -// ParticleData *pa=psys->particles+pa_num; -// ParticleReactEvent *re=psys->reactevents.first; -// int birth=0; -// float dist=0.0f; -// -// for(re=psys->reactevents.first; re; re=re->next){ -// birth=0; -// if(part->from==PART_FROM_PARTICLE){ -// if(pa->num==re->pa_num && pa->alive==PARS_UNBORN){ -// if(re->event==PART_EVENT_NEAR){ -// ParticleData *tpa = re->psys->particles+re->pa_num; -// float pa_time=tpa->time + pa->foffset*tpa->lifetime; -// if(re->time >= pa_time){ -// pa->time=pa_time; -// pa->dietime=pa->time+pa->lifetime; -// } -// } -// else{ -// pa->time=re->time; -// pa->dietime=pa->time+pa->lifetime; -// } -// } -// } -// else{ -// dist=len_v3v3(pa->state.co, re->state.co); -// if(dist <= re->size){ -// if(pa->alive==PARS_UNBORN){ -// pa->time=re->time; -// pa->dietime=pa->time+pa->lifetime; -// birth=1; -// } -// if(birth || part->flag&PART_REACT_MULTIPLE){ -// float vec[3]; -// VECSUB(vec,pa->state.co, re->state.co); -// if(birth==0) -// mul_v3_fl(vec,(float)pow(1.0f-dist/re->size,part->reactshape)); -// VECADDFAC(pa->state.vel,pa->state.vel,vec,part->reactfac); -// VECADDFAC(pa->state.vel,pa->state.vel,re->state.vel,part->partfac); -// } -// if(birth) -// mul_v3_fl(pa->state.vel,(float)pow(1.0f-dist/re->size,part->reactshape)); -// } -// } -// } -//} -//void psys_get_reactor_target(ParticleSimulationData *sim, Object **target_ob, ParticleSystem **target_psys) -//{ -// Object *tob; -// -// tob = sim->psys->target_ob ? sim->psys->target_ob : sim->ob; -// -// *target_psys = BLI_findlink(&tob->particlesystem, sim->psys->target_psys-1); -// if(*target_psys) -// *target_ob=tob; -// else -// *target_ob=0; -//} + /************************************************/ /* Point Cache */ /************************************************/ @@ -2174,17 +1989,48 @@ void psys_get_pointcache_start_end(Scene *scene, ParticleSystem *psys, int *sfra /************************************************/ /* Effectors */ /************************************************/ +static void psys_update_particle_bvhtree(ParticleSystem *psys, float cfra) +{ + if(psys) { + PARTICLE_P; + int totpart = 0; + + if(!psys->bvhtree || psys->bvhtree_frame != cfra) { + LOOP_SHOWN_PARTICLES { + totpart++; + } + + BLI_bvhtree_free(psys->bvhtree); + psys->bvhtree = BLI_bvhtree_new(totpart, 0.0, 4, 6); + + LOOP_SHOWN_PARTICLES { + if(pa->alive == PARS_ALIVE) { + if(pa->state.time == cfra) + BLI_bvhtree_insert(psys->bvhtree, p, pa->prev_state.co, 1); + else + BLI_bvhtree_insert(psys->bvhtree, p, pa->state.co, 1); + } + } + BLI_bvhtree_balance(psys->bvhtree); + + psys->bvhtree_frame = cfra; + } + } +} void psys_update_particle_tree(ParticleSystem *psys, float cfra) { if(psys) { PARTICLE_P; + int totpart = 0; if(!psys->tree || psys->tree_frame != cfra) { - - BLI_kdtree_free(psys->tree); + LOOP_SHOWN_PARTICLES { + totpart++; + } + BLI_kdtree_free(psys->tree); psys->tree = BLI_kdtree_new(psys->totpart); - + LOOP_SHOWN_PARTICLES { if(pa->alive == PARS_ALIVE) { if(pa->state.time == cfra) @@ -2195,7 +2041,7 @@ void psys_update_particle_tree(ParticleSystem *psys, float cfra) } BLI_kdtree_balance(psys->tree); - psys->tree_frame = psys->cfra; + psys->tree_frame = cfra; } } } @@ -2207,6 +2053,128 @@ static void psys_update_effectors(ParticleSimulationData *sim) precalc_guides(sim, sim->psys->effectors); } +static void integrate_particle(ParticleSettings *part, ParticleData *pa, float dtime, float *external_acceleration, void (*force_func)(void *forcedata, ParticleKey *state, float *force, float *impulse), void *forcedata) +{ + ParticleKey states[5]; + float force[3],acceleration[3],impulse[3],dx[4][3],dv[4][3],oldpos[3]; + float pa_mass= (part->flag & PART_SIZEMASS ? part->mass * pa->size : part->mass); + int i, steps=1; + + copy_v3_v3(oldpos, pa->state.co); + + switch(part->integrator){ + case PART_INT_EULER: + steps=1; + break; + case PART_INT_MIDPOINT: + steps=2; + break; + case PART_INT_RK4: + steps=4; + break; + case PART_INT_VERLET: + steps=1; + break; + } + + copy_particle_key(states, &pa->state, 1); + + states->time = 0.f; + + for(i=0; i<steps; i++){ + zero_v3(force); + zero_v3(impulse); + + force_func(forcedata, states+i, force, impulse); + + /* force to acceleration*/ + mul_v3_v3fl(acceleration, force, 1.0f/pa_mass); + + if(external_acceleration) + add_v3_v3(acceleration, external_acceleration); + + /* calculate next state */ + add_v3_v3(states[i].vel, impulse); + + switch(part->integrator){ + case PART_INT_EULER: + madd_v3_v3v3fl(pa->state.co, states->co, states->vel, dtime); + madd_v3_v3v3fl(pa->state.vel, states->vel, acceleration, dtime); + break; + case PART_INT_MIDPOINT: + if(i==0){ + madd_v3_v3v3fl(states[1].co, states->co, states->vel, dtime*0.5f); + madd_v3_v3v3fl(states[1].vel, states->vel, acceleration, dtime*0.5f); + states[1].time = dtime*0.5f; + /*fra=sim->psys->cfra+0.5f*dfra;*/ + } + else{ + madd_v3_v3v3fl(pa->state.co, states->co, states[1].vel, dtime); + madd_v3_v3v3fl(pa->state.vel, states->vel, acceleration, dtime); + } + break; + case PART_INT_RK4: + switch(i){ + case 0: + copy_v3_v3(dx[0], states->vel); + mul_v3_fl(dx[0], dtime); + copy_v3_v3(dv[0], acceleration); + mul_v3_fl(dv[0], dtime); + + madd_v3_v3v3fl(states[1].co, states->co, dx[0], 0.5f); + madd_v3_v3v3fl(states[1].vel, states->vel, dv[0], 0.5f); + states[1].time = dtime*0.5f; + /*fra=sim->psys->cfra+0.5f*dfra;*/ + break; + case 1: + madd_v3_v3v3fl(dx[1], states->vel, dv[0], 0.5f); + mul_v3_fl(dx[1], dtime); + copy_v3_v3(dv[1], acceleration); + mul_v3_fl(dv[1], dtime); + + madd_v3_v3v3fl(states[2].co, states->co, dx[1], 0.5f); + madd_v3_v3v3fl(states[2].vel, states->vel, dv[1], 0.5f); + states[2].time = dtime*0.5f; + break; + case 2: + madd_v3_v3v3fl(dx[2], states->vel, dv[1], 0.5f); + mul_v3_fl(dx[2], dtime); + copy_v3_v3(dv[2], acceleration); + mul_v3_fl(dv[2], dtime); + + add_v3_v3v3(states[3].co, states->co, dx[2]); + add_v3_v3v3(states[3].vel, states->vel, dv[2]); + states[3].time = dtime; + /*fra=cfra;*/ + break; + case 3: + add_v3_v3v3(dx[3], states->vel, dv[2]); + mul_v3_fl(dx[3], dtime); + copy_v3_v3(dv[3], acceleration); + mul_v3_fl(dv[3], dtime); + + madd_v3_v3v3fl(pa->state.co, states->co, dx[0], 1.0f/6.0f); + madd_v3_v3fl(pa->state.co, dx[1], 1.0f/3.0f); + madd_v3_v3fl(pa->state.co, dx[2], 1.0f/3.0f); + madd_v3_v3fl(pa->state.co, dx[3], 1.0f/6.0f); + + madd_v3_v3v3fl(pa->state.vel, states->vel, dv[0], 1.0f/6.0f); + madd_v3_v3fl(pa->state.vel, dv[1], 1.0f/3.0f); + madd_v3_v3fl(pa->state.vel, dv[2], 1.0f/3.0f); + madd_v3_v3fl(pa->state.vel, dv[3], 1.0f/6.0f); + } + break; + case PART_INT_VERLET: /* Verlet integration */ + madd_v3_v3v3fl(pa->state.vel, pa->prev_state.vel, acceleration, dtime); + madd_v3_v3v3fl(pa->state.co, pa->prev_state.co, pa->state.vel, dtime); + + sub_v3_v3v3(pa->state.vel, pa->state.co, oldpos); + mul_v3_fl(pa->state.vel, 1.0f/dtime); + break; + } + } +} + /********************************************************************************************************* SPH fluid physics @@ -2222,7 +2190,7 @@ static void psys_update_effectors(ParticleSimulationData *sim) ***********************************************************************************************************/ #define PSYS_FLUID_SPRINGS_INITIAL_SIZE 256 -static ParticleSpring *add_fluid_spring(ParticleSystem *psys, ParticleSpring *spring) +static ParticleSpring *sph_spring_add(ParticleSystem *psys, ParticleSpring *spring) { /* Are more refs required? */ if(psys->alloc_fluidsprings == 0 || psys->fluid_springs == NULL) { @@ -2240,8 +2208,7 @@ static ParticleSpring *add_fluid_spring(ParticleSystem *psys, ParticleSpring *sp return psys->fluid_springs + psys->tot_fluidsprings - 1; } - -static void delete_fluid_spring(ParticleSystem *psys, int j) +static void sph_spring_delete(ParticleSystem *psys, int j) { if (j != psys->tot_fluidsprings - 1) psys->fluid_springs[j] = psys->fluid_springs[psys->tot_fluidsprings - 1]; @@ -2253,8 +2220,52 @@ static void delete_fluid_spring(ParticleSystem *psys, int j) psys->fluid_springs = (ParticleSpring*)MEM_reallocN(psys->fluid_springs, psys->alloc_fluidsprings * sizeof(ParticleSpring)); } } +static void sph_springs_modify(ParticleSystem *psys, float dtime){ + SPHFluidSettings *fluid = psys->part->fluid; + ParticleData *pa1, *pa2; + ParticleSpring *spring = psys->fluid_springs; + + float h, d, Rij[3], rij, Lij; + int i; + + float yield_ratio = fluid->yield_ratio; + float plasticity = fluid->plasticity_constant; + /* scale things according to dtime */ + float timefix = 25.f * dtime; + + if((fluid->flag & SPH_VISCOELASTIC_SPRINGS)==0 || fluid->spring_k == 0.f) + return; -static EdgeHash *build_fluid_springhash(ParticleSystem *psys) + /* Loop through the springs */ + for(i=0; i<psys->tot_fluidsprings; i++, spring++) { + pa1 = psys->particles + spring->particle_index[0]; + pa2 = psys->particles + spring->particle_index[1]; + + sub_v3_v3v3(Rij, pa2->prev_state.co, pa1->prev_state.co); + rij = normalize_v3(Rij); + + /* adjust rest length */ + Lij = spring->rest_length; + d = yield_ratio * timefix * Lij; + + if (rij > Lij + d) // Stretch + spring->rest_length += plasticity * (rij - Lij - d) * timefix; + else if(rij < Lij - d) // Compress + spring->rest_length -= plasticity * (Lij - d - rij) * timefix; + + h = 4.f*pa1->size; + + if(spring->rest_length > h) + spring->delete_flag = 1; + } + + /* Loop through springs backwaqrds - for efficient delete function */ + for (i=psys->tot_fluidsprings-1; i >= 0; i--) { + if(psys->fluid_springs[i].delete_flag) + sph_spring_delete(psys, i); + } +} +static EdgeHash *sph_springhash_build(ParticleSystem *psys) { EdgeHash *springhash = NULL; ParticleSpring *spring; @@ -2267,347 +2278,275 @@ static EdgeHash *build_fluid_springhash(ParticleSystem *psys) return springhash; } -static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *pa, float dtime, float mass, float *gravity, EdgeHash *springhash) + +typedef struct SPHNeighbor { - SPHFluidSettings *fluid = psys->part->fluid; - KDTreeNearest *ptn = NULL; - ParticleData *npa; + ParticleSystem *psys; + int index; +} SPHNeighbor; +typedef struct SPHRangeData +{ + SPHNeighbor neighbors[128]; + int tot_neighbors; + + float density, near_density; + float h; + + ParticleSystem *npsys; + ParticleData *pa; + + float massfac; + int use_size; +} SPHRangeData; +typedef struct SPHData { + ParticleSystem *psys[10]; + ParticleData *pa; + float mass; + EdgeHash *eh; + float *gravity; +}SPHData; +static void sph_density_accum_cb(void *userdata, int index, float squared_dist) +{ + SPHRangeData *pfr = (SPHRangeData *)userdata; + ParticleData *npa = pfr->npsys->particles + index; + float q; + + if(npa == pfr->pa || squared_dist < FLT_EPSILON) + return; + + /* Ugh! One particle has over 128 neighbors! Really shouldn't happen, + * but even if it does it shouldn't do any terrible harm if all are + * not taken into account - jahka + */ + if(pfr->tot_neighbors >= 128) + return; + + pfr->neighbors[pfr->tot_neighbors].index = index; + pfr->neighbors[pfr->tot_neighbors].psys = pfr->npsys; + pfr->tot_neighbors++; + + q = (1.f - sqrt(squared_dist)/pfr->h) * pfr->massfac; + + if(pfr->use_size) + q *= npa->size; + + pfr->density += q*q; + pfr->near_density += q*q*q; +} +static void sph_force_cb(void *sphdata_v, ParticleKey *state, float *force, float *UNUSED(impulse)) +{ + SPHData *sphdata = (SPHData *)sphdata_v; + ParticleSystem **psys = sphdata->psys; + ParticleData *pa = sphdata->pa; + SPHFluidSettings *fluid = psys[0]->part->fluid; ParticleSpring *spring = NULL; + SPHRangeData pfr; + SPHNeighbor *pfn; + float mass = sphdata->mass; + float *gravity = sphdata->gravity; + EdgeHash *springhash = sphdata->eh; - float temp[3]; - float q, q1, u, I, D, rij, d, Lij; - float pressure_near, pressure; - float p=0, pnear=0; + float q, u, rij, dv[3]; + float pressure, near_pressure; - float omega = fluid->viscosity_omega; - float beta = fluid->viscosity_beta; - float massfactor = 1.0f/mass; - float spring_k = fluid->spring_k; - float h = fluid->radius; - float L = fluid->rest_length * fluid->radius; + float visc = fluid->viscosity_omega; + float stiff_visc = fluid->viscosity_beta * (fluid->flag & SPH_FAC_VISCOSITY ? fluid->viscosity_omega : 1.f); - int n, neighbours = BLI_kdtree_range_search(psys->tree, h, pa->prev_state.co, NULL, &ptn); - int spring_index = 0, index = own_psys ? pa - psys->particles : -1; + float inv_mass = 1.0f/mass; + float spring_constant = fluid->spring_k; + + float h = fluid->radius * (fluid->flag & SPH_FAC_RADIUS ? 4.f*pa->size : 1.f); /* 4.0 seems to be a pretty good value */ + float rest_density = fluid->rest_density * (fluid->flag & SPH_FAC_DENSITY ? 4.77f : 1.f); /* 4.77 is an experimentally determined density factor */ + float rest_length = fluid->rest_length * (fluid->flag & SPH_FAC_REST_LENGTH ? 2.588f * pa->size : 1.f); - /* pressure and near pressure */ - for(n=own_psys?1:0; n<neighbours; n++) { - /* disregard particles at the exact same location */ - if(ptn[n].dist < FLT_EPSILON) - continue; + float stiffness = fluid->stiffness_k; + float stiffness_near_fac = fluid->stiffness_knear * (fluid->flag & SPH_FAC_REPULSION ? fluid->stiffness_k : 1.f); - sub_v3_v3(ptn[n].co, pa->prev_state.co); - mul_v3_fl(ptn[n].co, 1.f/ptn[n].dist); - q = ptn[n].dist/h; + ParticleData *npa; + float vec[3]; + float vel[3]; + float co[3]; - if(q < 1.f) { - q1 = 1.f - q; + int i, spring_index, index = pa - psys[0]->particles; - p += q1*q1; - pnear += q1*q1*q1; - } + pfr.tot_neighbors = 0; + pfr.density = pfr.near_density = 0.f; + pfr.h = h; + pfr.pa = pa; + + for(i=0; i<10 && psys[i]; i++) { + pfr.npsys = psys[i]; + pfr.massfac = psys[i]->part->mass*inv_mass; + pfr.use_size = psys[i]->part->flag & PART_SIZEMASS; + + BLI_bvhtree_range_query(psys[i]->bvhtree, state->co, h, sph_density_accum_cb, &pfr); } - p *= mass; - pnear *= mass; - pressure = fluid->stiffness_k * (p - fluid->rest_density); - pressure_near = fluid->stiffness_knear * pnear; + pressure = stiffness * (pfr.density - rest_density); + near_pressure = stiffness_near_fac * pfr.near_density; - /* main calculations */ - for(n=own_psys?1:0; n<neighbours; n++) { - /* disregard particles at the exact same location */ - if(ptn[n].dist < FLT_EPSILON) - continue; + pfn = pfr.neighbors; + for(i=0; i<pfr.tot_neighbors; i++, pfn++) { + npa = pfn->psys->particles + pfn->index; - npa = psys->particles + ptn[n].index; + madd_v3_v3v3fl(co, npa->prev_state.co, npa->prev_state.vel, state->time); - rij = ptn[n].dist; - q = rij/h; - q1 = 1.f-q; + sub_v3_v3v3(vec, co, state->co); + rij = normalize_v3(vec); - /* Double Density Relaxation - Algorithm 2 (can't be thread safe!)*/ - D = dtime * dtime * (pressure + pressure_near*q1)*q1 * 0.5f; - madd_v3_v3fl(pa->state.co, ptn[n].co, -D * massfactor); - if(own_psys) - madd_v3_v3fl(npa->state.co, ptn[n].co, D * massfactor); + q = (1.f - rij/h) * pfn->psys->part->mass * inv_mass; - if(index < ptn[n].index) { - /* Viscosity - Algorithm 5 */ - if(omega > 0.f || beta > 0.f) { - sub_v3_v3v3(temp, pa->state.vel, npa->state.vel); - u = dot_v3v3(ptn[n].co, temp); + if(pfn->psys->part->flag & PART_SIZEMASS) + q *= npa->size; - if (u > 0){ - I = dtime * (q1 * (omega * u + beta * u*u)) * 0.5f; - madd_v3_v3fl(pa->state.vel, ptn[n].co, -I * massfactor); + copy_v3_v3(vel, npa->prev_state.vel); - if(own_psys) - madd_v3_v3fl(npa->state.vel, ptn[n].co, I * massfactor); - } - } + /* Double Density Relaxation */ + madd_v3_v3fl(force, vec, -(pressure + near_pressure*q)*q); - if(spring_k > 0.f) { - /* Viscoelastic spring force - Algorithm 4*/ - if (fluid->flag & SPH_VISCOELASTIC_SPRINGS && springhash){ - spring_index = GET_INT_FROM_POINTER(BLI_edgehash_lookup(springhash, index, ptn[n].index)); + /* Viscosity */ + if(visc > 0.f || stiff_visc > 0.f) { + sub_v3_v3v3(dv, vel, state->vel); + u = dot_v3v3(vec, dv); - if(spring_index) { - spring = psys->fluid_springs + spring_index - 1; - } - else { - ParticleSpring temp_spring; - temp_spring.particle_index[0] = index; - temp_spring.particle_index[1] = ptn[n].index; - temp_spring.rest_length = (fluid->flag & SPH_CURRENT_REST_LENGTH) ? rij : L; - temp_spring.delete_flag = 0; - - spring = add_fluid_spring(psys, &temp_spring); - } + if(u < 0.f && visc > 0.f) + madd_v3_v3fl(force, vec, 0.5f * q * visc * u ); + + if(u > 0.f && stiff_visc > 0.f) + madd_v3_v3fl(force, vec, 0.5f * q * stiff_visc * u ); + } + + if(spring_constant > 0.f) { + /* Viscoelastic spring force */ + if (pfn->psys == psys[0] && fluid->flag & SPH_VISCOELASTIC_SPRINGS && springhash) { + spring_index = GET_INT_FROM_POINTER(BLI_edgehash_lookup(springhash, index, pfn->index)); - Lij = spring->rest_length; - d = fluid->yield_ratio * Lij; + if(spring_index) { + spring = psys[0]->fluid_springs + spring_index - 1; - if (rij > Lij + d) // Stretch, 25 is just a multiplier for plasticity_constant value to counter default dtime of 1/25 - spring->rest_length += dtime * 25.f * fluid->plasticity_constant * (rij - Lij - d); - else if(rij < Lij - d) // Compress - spring->rest_length -= dtime * 25.f * fluid->plasticity_constant * (Lij - d - rij); + madd_v3_v3fl(force, vec, -10.f * spring_constant * (1.f - rij/h) * (spring->rest_length - rij)); } - else { /* PART_SPRING_HOOKES - Hooke's spring force */ - /* L is a factor of radius */ - D = 0.5 * dtime * dtime * 10.f * fluid->spring_k * (1.f - L/h) * (L - rij); - - madd_v3_v3fl(pa->state.co, ptn[n].co, -D * massfactor); - if(own_psys) - madd_v3_v3fl(npa->state.co, ptn[n].co, D * massfactor); + else if(fluid->spring_frames == 0 || (pa->prev_state.time-pa->time) <= fluid->spring_frames){ + ParticleSpring temp_spring; + temp_spring.particle_index[0] = index; + temp_spring.particle_index[1] = pfn->index; + temp_spring.rest_length = (fluid->flag & SPH_CURRENT_REST_LENGTH) ? rij : rest_length; + temp_spring.delete_flag = 0; + + sph_spring_add(psys[0], &temp_spring); } } + else {/* PART_SPRING_HOOKES - Hooke's spring force */ + madd_v3_v3fl(force, vec, -10.f * spring_constant * (1.f - rij/h) * (rest_length - rij)); + } } - } - - /* Artificial buoyancy force in negative gravity direction */ - if (fluid->buoyancy >= 0.f && gravity) { - float B = -dtime * dtime * fluid->buoyancy * (p - fluid->rest_density) * 0.5f; - madd_v3_v3fl(pa->state.co, gravity, -B * massfactor); } - - if(ptn) - MEM_freeN(ptn); -} - -static void apply_particle_fluidsim(Object *ob, ParticleSystem *psys, ParticleData *pa, float dtime, float *gravity, EdgeHash *springhash){ - ParticleTarget *pt; - - particle_fluidsim(psys, 1, pa, dtime, psys->part->mass, gravity, springhash); - /*----check other SPH systems (Multifluids) , each fluid has its own parameters---*/ - for(pt=psys->targets.first; pt; pt=pt->next) { - ParticleSystem *epsys = psys_get_target_system(ob, pt); - - if(epsys) - particle_fluidsim(epsys, 0, pa, dtime, psys->part->mass, gravity, NULL); - } - /*----------------------------------------------------------------*/ + /* Artificial buoyancy force in negative gravity direction */ + if (fluid->buoyancy > 0.f && gravity) + madd_v3_v3fl(force, gravity, fluid->buoyancy * (pfr.density-rest_density)); } -static void apply_fluid_springs(ParticleSystem *psys, float timestep){ - SPHFluidSettings *fluid = psys->part->fluid; - ParticleData *pa1, *pa2; - ParticleSpring *spring = psys->fluid_springs; - - float h = fluid->radius; - float massfactor = 1.0f/psys->part->mass; - float D, Rij[3], rij, Lij; +static void sph_integrate(ParticleSimulationData *sim, ParticleData *pa, float dfra, float *gravity, EdgeHash *springhash){ + ParticleTarget *pt; int i; - if((fluid->flag & SPH_VISCOELASTIC_SPRINGS)==0 || fluid->spring_k == 0.f) - return; - - /* Loop through the springs */ - for(i=0; i<psys->tot_fluidsprings; i++, spring++) { - Lij = spring->rest_length; + ParticleSettings *part = sim->psys->part; + // float timestep = psys_get_timestep(sim); // UNUSED + float pa_mass = part->mass * (part->flag & PART_SIZEMASS ? pa->size : 1.f); + float dtime = dfra*psys_get_timestep(sim); + // int steps = 1; // UNUSED + float effector_acceleration[3]; + SPHData sphdata; - if (Lij > h) { - spring->delete_flag = 1; - } - else { - pa1 = psys->particles + spring->particle_index[0]; - pa2 = psys->particles + spring->particle_index[1]; + sphdata.psys[0] = sim->psys; + for(i=1, pt=sim->psys->targets.first; i<10; i++, pt=(pt?pt->next:NULL)) + sphdata.psys[i] = pt ? psys_get_target_system(sim->ob, pt) : NULL; - sub_v3_v3v3(Rij, pa2->prev_state.co, pa1->prev_state.co); - rij = normalize_v3(Rij); + sphdata.pa = pa; + sphdata.gravity = gravity; + sphdata.mass = pa_mass; + sphdata.eh = springhash; - /* Calculate displacement and apply value */ - D = 0.5f * timestep * timestep * 10.f * fluid->spring_k * (1.f - Lij/h) * (Lij - rij); + /* restore previous state and treat gravity & effectors as external acceleration*/ + sub_v3_v3v3(effector_acceleration, pa->state.vel, pa->prev_state.vel); + mul_v3_fl(effector_acceleration, 1.f/dtime); - madd_v3_v3fl(pa1->state.co, Rij, -D * pa1->state.time * pa1->state.time * massfactor); - madd_v3_v3fl(pa2->state.co, Rij, D * pa2->state.time * pa2->state.time * massfactor); - } - } + copy_particle_key(&pa->state, &pa->prev_state, 0); - /* Loop through springs backwaqrds - for efficient delete function */ - for (i=psys->tot_fluidsprings-1; i >= 0; i--) { - if(psys->fluid_springs[i].delete_flag) - delete_fluid_spring(psys, i); - } + integrate_particle(part, pa, dtime, effector_acceleration, sph_force_cb, &sphdata); } /************************************************/ -/* Newtonian physics */ +/* Basic physics */ /************************************************/ -/* gathers all forces that effect particles and calculates a new state for the particle */ -static void apply_particle_forces(ParticleSimulationData *sim, int p, float dfra, float cfra) +typedef struct EfData { + ParticleTexture ptex; + ParticleSimulationData *sim; + ParticleData *pa; +} EfData; +static void basic_force_cb(void *efdata_v, ParticleKey *state, float *force, float *impulse) +{ + EfData *efdata = (EfData *)efdata_v; + ParticleSimulationData *sim = efdata->sim; ParticleSettings *part = sim->psys->part; - ParticleData *pa = sim->psys->particles + p; + ParticleData *pa = efdata->pa; EffectedPoint epoint; - ParticleKey states[5], tkey; - float timestep = psys_get_timestep(sim); - float force[3],impulse[3],dx[4][3],dv[4][3],oldpos[3]; - float dtime=dfra*timestep, time, pa_mass=part->mass, fac /*, fra=sim->psys->cfra*/; - int i, steps=1; - ParticleTexture ptex; - psys_get_texture(sim, pa, &ptex, PAMAP_PHYSICS, cfra); - - /* maintain angular velocity */ - VECCOPY(pa->state.ave,pa->prev_state.ave); - VECCOPY(oldpos,pa->state.co); + /* add effectors */ + pd_point_from_particle(efdata->sim, efdata->pa, state, &epoint); + if(part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR) + pdDoEffectors(sim->psys->effectors, sim->colliders, part->effector_weights, &epoint, force, impulse); - if(part->flag & PART_SIZEMASS) - pa_mass*=pa->size; + mul_v3_fl(force, efdata->ptex.field); + mul_v3_fl(impulse, efdata->ptex.field); - switch(part->integrator){ - case PART_INT_EULER: - steps=1; - break; - case PART_INT_MIDPOINT: - steps=2; - break; - case PART_INT_RK4: - steps=4; - break; - case PART_INT_VERLET: - steps=1; - break; - } - - copy_particle_key(states,&pa->state,1); - - for(i=0; i<steps; i++){ - force[0]=force[1]=force[2]=0.0; - impulse[0]=impulse[1]=impulse[2]=0.0; - /* add effectors */ - pd_point_from_particle(sim, pa, states+i, &epoint); - if(part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR) - pdDoEffectors(sim->psys->effectors, sim->colliders, part->effector_weights, &epoint, force, impulse); - - mul_v3_fl(force, ptex.field); - mul_v3_fl(impulse, ptex.field); - - /* calculate air-particle interaction */ - if(part->dragfac!=0.0f){ - fac=-part->dragfac*pa->size*pa->size*len_v3(states[i].vel); - VECADDFAC(force,force,states[i].vel,fac); - } + /* calculate air-particle interaction */ + if(part->dragfac != 0.0f) + madd_v3_v3fl(force, state->vel, -part->dragfac * pa->size * pa->size * len_v3(state->vel)); - /* brownian force */ - if(part->brownfac!=0.0){ - force[0]+=(BLI_frand()-0.5f)*part->brownfac; - force[1]+=(BLI_frand()-0.5f)*part->brownfac; - force[2]+=(BLI_frand()-0.5f)*part->brownfac; - } - - /* force to acceleration*/ - mul_v3_fl(force,1.0f/pa_mass); - - /* add global acceleration (gravitation) */ - if(psys_uses_gravity(sim) - /* normal gravity is too strong for hair so it's disabled by default */ - && (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR)) { - madd_v3_v3fl(force, sim->scene->physics_settings.gravity, part->effector_weights->global_gravity * ptex.gravity); - } - - /* calculate next state */ - VECADD(states[i].vel,states[i].vel,impulse); + /* brownian force */ + if(part->brownfac != 0.0){ + force[0] += (BLI_frand()-0.5f) * part->brownfac; + force[1] += (BLI_frand()-0.5f) * part->brownfac; + force[2] += (BLI_frand()-0.5f) * part->brownfac; + } +} +/* gathers all forces that effect particles and calculates a new state for the particle */ +static void basic_integrate(ParticleSimulationData *sim, int p, float dfra, float cfra) +{ + ParticleSettings *part = sim->psys->part; + ParticleData *pa = sim->psys->particles + p; + ParticleKey tkey; + float dtime=dfra*psys_get_timestep(sim), time; + float *gravity = NULL, gr[3]; + EfData efdata; - switch(part->integrator){ - case PART_INT_EULER: - VECADDFAC(pa->state.co,states->co,states->vel,dtime); - VECADDFAC(pa->state.vel,states->vel,force,dtime); - break; - case PART_INT_MIDPOINT: - if(i==0){ - VECADDFAC(states[1].co,states->co,states->vel,dtime*0.5f); - VECADDFAC(states[1].vel,states->vel,force,dtime*0.5f); - /*fra=sim->psys->cfra+0.5f*dfra;*/ - } - else{ - VECADDFAC(pa->state.co,states->co,states[1].vel,dtime); - VECADDFAC(pa->state.vel,states->vel,force,dtime); - } - break; - case PART_INT_RK4: - switch(i){ - case 0: - VECCOPY(dx[0],states->vel); - mul_v3_fl(dx[0],dtime); - VECCOPY(dv[0],force); - mul_v3_fl(dv[0],dtime); + psys_get_texture(sim, pa, &efdata.ptex, PAMAP_PHYSICS, cfra); - VECADDFAC(states[1].co,states->co,dx[0],0.5f); - VECADDFAC(states[1].vel,states->vel,dv[0],0.5f); - /*fra=sim->psys->cfra+0.5f*dfra;*/ - break; - case 1: - VECADDFAC(dx[1],states->vel,dv[0],0.5f); - mul_v3_fl(dx[1],dtime); - VECCOPY(dv[1],force); - mul_v3_fl(dv[1],dtime); + efdata.pa = pa; + efdata.sim = sim; - VECADDFAC(states[2].co,states->co,dx[1],0.5f); - VECADDFAC(states[2].vel,states->vel,dv[1],0.5f); - break; - case 2: - VECADDFAC(dx[2],states->vel,dv[1],0.5f); - mul_v3_fl(dx[2],dtime); - VECCOPY(dv[2],force); - mul_v3_fl(dv[2],dtime); + /* add global acceleration (gravitation) */ + if(psys_uses_gravity(sim) + /* normal gravity is too strong for hair so it's disabled by default */ + && (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR)) { + zero_v3(gr); + madd_v3_v3fl(gr, sim->scene->physics_settings.gravity, part->effector_weights->global_gravity * efdata.ptex.gravity); + gravity = gr; + } - VECADD(states[3].co,states->co,dx[2]); - VECADD(states[3].vel,states->vel,dv[2]); - /*fra=cfra;*/ - break; - case 3: - VECADD(dx[3],states->vel,dv[2]); - mul_v3_fl(dx[3],dtime); - VECCOPY(dv[3],force); - mul_v3_fl(dv[3],dtime); - - VECADDFAC(pa->state.co,states->co,dx[0],1.0f/6.0f); - VECADDFAC(pa->state.co,pa->state.co,dx[1],1.0f/3.0f); - VECADDFAC(pa->state.co,pa->state.co,dx[2],1.0f/3.0f); - VECADDFAC(pa->state.co,pa->state.co,dx[3],1.0f/6.0f); - - VECADDFAC(pa->state.vel,states->vel,dv[0],1.0f/6.0f); - VECADDFAC(pa->state.vel,pa->state.vel,dv[1],1.0f/3.0f); - VECADDFAC(pa->state.vel,pa->state.vel,dv[2],1.0f/3.0f); - VECADDFAC(pa->state.vel,pa->state.vel,dv[3],1.0f/6.0f); - } - break; - case PART_INT_VERLET: /* Verlet integration */ - VECADDFAC(pa->state.vel,pa->state.vel,force,dtime); - VECADDFAC(pa->state.co,pa->state.co,pa->state.vel,dtime); + /* maintain angular velocity */ + copy_v3_v3(pa->state.ave, pa->prev_state.ave); - VECSUB(pa->state.vel,pa->state.co,oldpos); - mul_v3_fl(pa->state.vel,1.0f/dtime); - break; - } - } + integrate_particle(part, pa, dtime, gravity, basic_force_cb, &efdata); /* damp affects final velocity */ if(part->dampfac != 0.f) - mul_v3_fl(pa->state.vel, 1.f - part->dampfac * ptex.damp); + mul_v3_fl(pa->state.vel, 1.f - part->dampfac * efdata.ptex.damp); - VECCOPY(pa->state.ave, states->ave); + //VECCOPY(pa->state.ave, states->ave); /* finally we do guides */ time=(cfra-pa->time)/pa->lifetime; @@ -2627,7 +2566,7 @@ static void apply_particle_forces(ParticleSimulationData *sim, int p, float dfra } } } -static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra, float timestep) +static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, float timestep) { float rotfac, rot1[4], rot2[4]={1.0,0.0,0.0,0.0}, dtime=dfra*timestep; @@ -2665,6 +2604,9 @@ static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra normalize_qt(pa->state.rot); } +/************************************************/ +/* Collisions */ +/************************************************/ /* convert from triangle barycentric weights to quad mean value weights */ static void intersect_dm_quad_weights(float *v1, float *v2, float *v3, float *v4, float *w) { @@ -3156,7 +3098,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) if(!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { distribute_particles(sim, PART_FROM_CHILD); - if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0) + if(part->childtype==PART_CHILD_FACES && part->parents!=0.0) psys_find_parents(sim); } } @@ -3452,11 +3394,11 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra) case PART_PHYS_FLUID: { ParticleTarget *pt = psys->targets.first; - psys_update_particle_tree(psys, cfra); + psys_update_particle_bvhtree(psys, psys->cfra); for(; pt; pt=pt->next) { /* Updating others systems particle tree for fluid-fluid interaction */ if(pt->ob) - psys_update_particle_tree(BLI_findlink(&pt->ob->particlesystem, pt->psys-1), cfra); + psys_update_particle_bvhtree(BLI_findlink(&pt->ob->particlesystem, pt->psys-1), psys->cfra); } break; } @@ -3508,14 +3450,14 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra) { LOOP_DYNAMIC_PARTICLES { /* do global forces & effectors */ - apply_particle_forces(sim, p, pa->state.time, cfra); + basic_integrate(sim, p, pa->state.time, cfra); /* deflection */ if(sim->colliders) deflect_particle(sim, p, pa->state.time, cfra); /* rotations */ - rotate_particle(part, pa, pa->state.time, timestep); + basic_rotate(part, pa, pa->state.time, timestep); } break; } @@ -3538,43 +3480,28 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra) } case PART_PHYS_FLUID: { - EdgeHash *springhash = build_fluid_springhash(psys); + EdgeHash *springhash = sph_springhash_build(psys); float *gravity = NULL; if(psys_uses_gravity(sim)) gravity = sim->scene->physics_settings.gravity; - /* do global forces & effectors */ - LOOP_DYNAMIC_PARTICLES { - apply_particle_forces(sim, p, pa->state.time, cfra); - /* in fluids forces only effect velocity */ - copy_v3_v3(pa->state.co, pa->prev_state.co); - } - - /* actual fluids calculations (not threadsafe!) */ - LOOP_DYNAMIC_PARTICLES { - apply_particle_fluidsim(sim->ob, psys, pa, pa->state.time*timestep, gravity, springhash); - } - - /* Apply springs to particles */ - apply_fluid_springs(psys, timestep); - - /* apply velocity, collisions and rotation */ LOOP_DYNAMIC_PARTICLES { - /* velocity holds forces and viscosity, so apply them before collisions */ - madd_v3_v3fl(pa->state.co, pa->state.vel, pa->state.time*timestep); + /* do global forces & effectors */ + basic_integrate(sim, p, pa->state.time, cfra); - /* calculate new velocity based on new-old location */ - sub_v3_v3v3(pa->state.vel, pa->state.co, pa->prev_state.co); - mul_v3_fl(pa->state.vel, 1.f/(pa->state.time*timestep)); + /* actual fluids calculations */ + sph_integrate(sim, pa, pa->state.time, gravity, springhash); if(sim->colliders) deflect_particle(sim, p, pa->state.time, cfra); /* SPH particles are not physical particles, just interpolation particles, thus rotation has not a direct sense for them */ - rotate_particle(part, pa, pa->state.time, timestep); + basic_rotate(part, pa, pa->state.time, timestep); } + sph_springs_modify(psys, timestep); + if(springhash) { BLI_edgehash_free(springhash, NULL); springhash = NULL; @@ -3923,13 +3850,6 @@ static void psys_changed_type(ParticleSimulationData *sim) BKE_ptcache_id_from_particles(&pid, sim->ob, sim->psys); - if(part->from == PART_FROM_PARTICLE) { - //if(part->type != PART_REACTOR) - part->from = PART_FROM_FACE; - if(part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT) - part->distr = PART_DISTR_JIT; - } - if(part->phystype != PART_PHYS_KEYED) sim->psys->flag &= ~PSYS_KEYED; @@ -3937,6 +3857,9 @@ static void psys_changed_type(ParticleSimulationData *sim) if(ELEM4(part->ren_as, PART_DRAW_NOT, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR)==0) part->ren_as = PART_DRAW_PATH; + if(part->distr == PART_DISTR_GRID) + part->distr = PART_DISTR_JIT; + if(ELEM3(part->draw_as, PART_DRAW_NOT, PART_DRAW_REND, PART_DRAW_PATH)==0) part->draw_as = PART_DRAW_REND; @@ -3982,17 +3905,18 @@ void psys_check_boid_data(ParticleSystem *psys) static void fluid_default_settings(ParticleSettings *part){ SPHFluidSettings *fluid = part->fluid; - fluid->radius = 0.5f; fluid->spring_k = 0.f; fluid->plasticity_constant = 0.1f; fluid->yield_ratio = 0.1f; - fluid->rest_length = 0.5f; + fluid->rest_length = 1.f; fluid->viscosity_omega = 2.f; - fluid->viscosity_beta = 0.f; - fluid->stiffness_k = 0.1f; - fluid->stiffness_knear = 0.05f; - fluid->rest_density = 10.f; + fluid->viscosity_beta = 0.1f; + fluid->stiffness_k = 1.f; + fluid->stiffness_knear = 1.f; + fluid->rest_density = 1.f; fluid->buoyancy = 0.f; + fluid->radius = 1.f; + fluid->flag |= SPH_FAC_REPULSION|SPH_FAC_DENSITY|SPH_FAC_RADIUS|SPH_FAC_VISCOSITY|SPH_FAC_REST_LENGTH; } static void psys_prepare_physics(ParticleSimulationData *sim) |