Viscoelastic springs for sph particle fluids, original patch by Stephen Whitehorn...
authorJanne Karhu <jhkarh@gmail.com>
Sun, 9 Jan 2011 19:09:41 +0000 (19:09 +0000)
committerJanne Karhu <jhkarh@gmail.com>
Sun, 9 Jan 2011 19:09:41 +0000 (19:09 +0000)
* Viscoelastic springs between the fluid particles can simulate all kinds
  of viscous and elastic substances, such as jelly and honey. This is
  achieved by creating springs dynamically between neighboring particles
  and adjusting their rest length based on stretching/compression.
* This nearly completes the currently intended functionality for particle
  fluids. The last missing thing is a surfacing extraction algorithm,
  which is needed for a proper representation of a sph fluid.
* I also cleaned up and renamed some of the fluid parameters to make the
  ui a bit easier to understand.
* One addition to the patch is an option to use "initial rest length" for
  the springs, which uses the lengths between the particles at the time of
  spring creation as the spring rest lengths instead of interaction radius/2.
  This makes the fluid keep it's original shape better (good for very
  viscoelastic materials), but can create large density differences inside
  the fluid (not really physically correct for a fluid).
* Viscoelastic springs are stored in point cache as extra data.

release/scripts/ui/properties_particle.py
source/blender/blenkernel/BKE_pointcache.h
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/pointcache.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/makesdna/DNA_object_force.h
source/blender/makesdna/DNA_particle_types.h
source/blender/makesrna/intern/rna_particle.c

index 2c5cfeaca63fbaf0c24c4daf08ed95fb9095d3b5..cc4b974e6b6459c2518bec4fda2d04f25485378e 100644 (file)
@@ -445,24 +445,32 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
             split = layout.split()
             sub = split.column()
             sub.label(text="Fluid Interaction:")
-            sub.prop(fluid, "fluid_radius", slider=True)
-            sub.prop(fluid, "stiffness")
-            sub.prop(fluid, "stiffness_near")
-            sub.prop(fluid, "rest_density")
+            sub.prop(fluid, "fluid_radius")
+            sub.prop(fluid, "repulsion_force")
+            subsub = sub.column(align=True)
+            subsub.prop(fluid, "rest_density")
+            subsub.prop(fluid, "density_force", text="Force")
 
             sub.label(text="Viscosity:")
-            sub.prop(fluid, "viscosity_omega", text="Linear")
-            sub.prop(fluid, "viscosity_beta", text="Square")
+            subsub = sub.column(align=True)
+            subsub.prop(fluid, "linear_viscosity", text="Linear")
+            subsub.prop(fluid, "square_viscosity", text="Square")
 
             sub = split.column()
 
             sub.label(text="Springs:")
-            sub.prop(fluid, "spring_force", text="Force", slider=True)
-            sub.prop(fluid, "rest_length", slider=True)
-            layout.label(text="Multiple fluids interactions:")
+            sub.prop(fluid, "spring_force", text="Force")
+            #Hidden to make ui a bit lighter, can be unhidden for a bit more control
+            #sub.prop(fluid, "rest_length", slider=True)
+            sub.prop(fluid, "use_viscoelastic_springs")
+            subsub = sub.column(align=True)
+            subsub.active = fluid.use_viscoelastic_springs
+            subsub.prop(fluid, "yield_ratio", slider=True)
+            subsub.prop(fluid, "plasticity", slider=True)
+            subsub.prop(fluid, "use_initial_rest_length")
 
             sub.label(text="Buoyancy:")
-            sub.prop(fluid, "buoyancy", slider=True)
+            sub.prop(fluid, "buoyancy", text="Strength", slider=True)
 
         elif part.physics_type == 'KEYED':
             split = layout.split()
@@ -526,6 +534,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
         if part.physics_type == 'KEYED' or part.physics_type == 'BOIDS' or part.physics_type == 'FLUID':
             if part.physics_type == 'BOIDS':
                 layout.label(text="Relations:")
+            elif part.physics_type == 'FLUID':
+                layout.label(text="Fluid interaction:")
 
             row = layout.row()
             row.template_list(psys, "targets", psys, "active_particle_target_index")
@@ -886,6 +896,9 @@ class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
         col.prop(part, "show_number")
         if part.physics_type == 'BOIDS':
             col.prop(part, "show_health")
+        
+        
 
         col = row.column()
         col.prop(part, "show_material_color", text="Use material color")
index 68c4177fe3a35d3114f9c6910491bf78e2b35e35..a4ce46409a1c185fd1d65ac54048211ec656892f 100644 (file)
@@ -32,6 +32,7 @@
 #include "DNA_ID.h"
 #include "DNA_object_force.h"
 #include "DNA_boid_types.h"
+#include "DNA_particle_types.h"
 #include <stdio.h> /* for FILE */
 
 /* Point cache clearing option, for BKE_ptcache_id_clear, before
@@ -110,6 +111,16 @@ static char *ptcache_datastruct[] = {
        "BoidData" // case BPHYS_DATA_BOIDS:
 };
 
+static char *ptcache_extra_datastruct[] = {
+       "",
+       "ParticleSpring"
+};
+
+static int ptcache_extra_datasize[] = {
+       0,
+       sizeof(ParticleSpring)
+};
+
 typedef struct PTCacheFile {
        FILE *fp;
 
@@ -149,11 +160,11 @@ typedef struct PTCacheID {
        void (*read_stream)(PTCacheFile *pf, void *calldata);
 
        /* copies custom extradata to cache data */
-       int (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra);
+       void (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra);
        /* copies custom extradata to cache data */
-       int (*read_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra);
+       void (*read_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra);
        /* copies custom extradata to cache data */
-       int (*interpolate_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra, float cfra1, float cfra2);
+       void (*interpolate_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra, float cfra1, float cfra2);
 
        /* total number of simulated points (the cfra parameter is just for using same function pointer with totwrite) */
        int (*totpoint)(void *calldata, int cfra);
index 26f96d0c30487343a61b7e1c9201d4c64aee165b..279190b9f9c79568a3dc98af7c11bb845cfef96d 100644 (file)
@@ -561,6 +561,9 @@ void psys_free(Object *ob, ParticleSystem * psys)
                BLI_freelistN(&psys->targets);
 
                BLI_kdtree_free(psys->tree);
+               if(psys->fluid_springs)
+                       MEM_freeN(psys->fluid_springs);
 
                pdEndEffectors(&psys->effectors);
 
index 3534a7dba374916316c945994cc65f0e90e65f00..fac79270bc157bb02ec005464a17c1f10dca3228 100644 (file)
@@ -54,6 +54,7 @@
 #include "DNA_ipo_types.h" // XXX old animation system stuff... to be removed!
 #include "DNA_listBase.h"
 
+#include "BLI_edgehash.h"
 #include "BLI_rand.h"
 #include "BLI_jitter.h"
 #include "BLI_math.h"
@@ -182,6 +183,13 @@ void psys_reset(ParticleSystem *psys, int mode)
 
        /* reset point cache */
        BKE_ptcache_invalidate(psys->pointcache);
+
+       if(psys->fluid_springs) {
+               MEM_freeN(psys->fluid_springs);
+               psys->fluid_springs = NULL;
+       }
+
+       psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
 }
 
 static void realloc_particles(ParticleSimulationData *sim, int new_totpart)
@@ -2228,32 +2236,79 @@ static void psys_update_effectors(ParticleSimulationData *sim)
  Presented at Siggraph, (2005)
 
 ***********************************************************************************************************/
-static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *pa, float dtime, float mass, float *gravity)
+#define PSYS_FLUID_SPRINGS_INITIAL_SIZE 256
+ParticleSpring *add_fluid_spring(ParticleSystem *psys, ParticleSpring *spring)
+{
+       /* Are more refs required? */
+       if(psys->alloc_fluidsprings == 0 || psys->fluid_springs == NULL) {
+               psys->alloc_fluidsprings = PSYS_FLUID_SPRINGS_INITIAL_SIZE;
+               psys->fluid_springs = (ParticleSpring*)MEM_callocN(psys->alloc_fluidsprings * sizeof(ParticleSpring), "Particle Fluid Springs");
+       }
+       else if(psys->tot_fluidsprings == psys->alloc_fluidsprings) {
+               /* Double the number of refs allocated */
+               psys->alloc_fluidsprings *= 2;
+               psys->fluid_springs = (ParticleSpring*)MEM_reallocN(psys->fluid_springs, psys->alloc_fluidsprings * sizeof(ParticleSpring));
+       }
+
+       memcpy(psys->fluid_springs + psys->tot_fluidsprings, spring, sizeof(ParticleSpring));
+       psys->tot_fluidsprings++;
+
+       return psys->fluid_springs + psys->tot_fluidsprings - 1;
+}
+
+void  delete_fluid_spring(ParticleSystem *psys, int j)
+{
+       if (j != psys->tot_fluidsprings - 1)
+               psys->fluid_springs[j] = psys->fluid_springs[psys->tot_fluidsprings - 1];
+
+       psys->tot_fluidsprings--;
+
+       if (psys->tot_fluidsprings < psys->alloc_fluidsprings/2 && psys->alloc_fluidsprings > PSYS_FLUID_SPRINGS_INITIAL_SIZE){
+               psys->alloc_fluidsprings /= 2;
+               psys->fluid_springs = (ParticleSpring*)MEM_reallocN(psys->fluid_springs,  psys->alloc_fluidsprings * sizeof(ParticleSpring));
+       }
+}
+
+EdgeHash *build_fluid_springhash(ParticleSystem *psys)
+{
+       EdgeHash *springhash = NULL;
+       ParticleSpring *spring = psys->fluid_springs;
+       int i = 0;
+
+       springhash = BLI_edgehash_new();
+
+       for(i=0, spring=psys->fluid_springs; i<psys->tot_fluidsprings; i++, spring++)
+               BLI_edgehash_insert(springhash, spring->particle_index[0], spring->particle_index[1], SET_INT_IN_POINTER(i+1));
+
+       return springhash;
+}
+static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *pa, float dtime, float mass, float *gravity, EdgeHash *springhash)
 {
        SPHFluidSettings *fluid = psys->part->fluid;
        KDTreeNearest *ptn = NULL;
        ParticleData *npa;
+       ParticleSpring *spring = NULL;
 
        float temp[3];
-       float q, q1, u, I, D;
+       float q, q1, u, I, D, rij, d, Lij;
        float pressure_near, pressure;
        float p=0, pnear=0;
 
-       float radius = fluid->radius;
        float omega = fluid->viscosity_omega;
        float beta = fluid->viscosity_beta;
        float massfactor = 1.0f/mass;
        float spring_k = fluid->spring_k;
-       float L = fluid->rest_length;
+       float h = fluid->radius;
+       float L = fluid->rest_length * fluid->radius;
 
-       int n, neighbours = BLI_kdtree_range_search(psys->tree, radius, pa->prev_state.co, NULL, &ptn);
-       int index = own_psys ? pa - psys->particles : -1;
+       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;
 
        /* pressure and near pressure */
        for(n=own_psys?1:0; n<neighbours; n++) {
                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/radius;
+               q = ptn[n].dist/h;
 
                if(q < 1.f) {
                        q1 = 1.f - q;
@@ -2272,7 +2327,8 @@ static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *
        for(n=own_psys?1:0; n<neighbours; n++) {
                npa = psys->particles + ptn[n].index;
 
-               q = ptn[n].dist/radius;
+               rij = ptn[n].dist;
+               q = rij/h;
                q1 = 1.f-q;
 
                /* Double Density Relaxation - Algorithm 2 (can't be thread safe!)*/
@@ -2296,14 +2352,40 @@ static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *
                                }
                        }
 
-                       /* Hooke's spring force */
                        if(spring_k > 0.f) {
-                               /* L is a factor of radius */
-                               D = 0.5 * dtime * dtime * 10.f * fluid->spring_k * (1.f - L) * (L - q);
+                               /* 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));
+
+                                       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);
+                                       }
+
+                                       Lij = spring->rest_length;
+                                       d = fluid->yield_ratio * Lij;
 
-                               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);
+                                       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);
+                               }
+                               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);
+                               }
                        }
                }
        } 
@@ -2318,21 +2400,63 @@ static void particle_fluidsim(ParticleSystem *psys, int own_psys, ParticleData *
                MEM_freeN(ptn);
 }
 
-static void apply_particle_fluidsim(Object *ob, ParticleSystem *psys, ParticleData *pa, float dtime, float *gravity){
+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);
+       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);
+                       particle_fluidsim(epsys, 0, pa, dtime, psys->part->mass, gravity, NULL);
        }
        /*----------------------------------------------------------------*/             
 }
 
+static void apply_fluid_springs(ParticleSystem *psys, ParticleSettings *part, 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;
+       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;
+
+               if (Lij > h) {
+                       spring->delete_flag = 1;
+               }
+               else {
+                       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);
+
+                       /* Calculate displacement and apply value */
+                       D =  0.5f * timestep * timestep * 10.f * fluid->spring_k * (1.f - Lij/h) * (Lij - rij);
+
+                       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);
+               }
+       }
+
+       /* 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);
+       }
+}
+
 /************************************************/
 /*                     Newtonian physics                                       */
 /************************************************/
@@ -3420,6 +3544,7 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
                }
                case PART_PHYS_FLUID:
                {
+                       EdgeHash *springhash = build_fluid_springhash(psys);
                        float *gravity = NULL;
 
                        if(psys_uses_gravity(sim))
@@ -3434,9 +3559,12 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
 
                        /* actual fluids calculations (not threadsafe!) */
                        LOOP_DYNAMIC_PARTICLES {
-                               apply_particle_fluidsim(sim->ob, psys, pa, pa->state.time*timestep, gravity);
+                               apply_particle_fluidsim(sim->ob, psys, pa, pa->state.time*timestep, gravity, springhash);
                        }
 
+                       /* Apply springs to particles */
+                       apply_fluid_springs(psys, part, timestep);
+
                        /* apply velocity, collisions and rotation */
                        LOOP_DYNAMIC_PARTICLES {
                                /* velocity holds forces and viscosity, so apply them before collisions */
@@ -3452,6 +3580,11 @@ static void dynamics_step(ParticleSimulationData *sim, float 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);  
                        }
+
+                       if(springhash) {
+                               BLI_edgehash_free(springhash, NULL);
+                               springhash = NULL;
+                       }
                        break;
                }
        }
@@ -3696,6 +3829,13 @@ static void system_step(ParticleSimulationData *sim, float cfra)
                /* reset only just created particles (on startframe all particles are recreated) */
                reset_all_particles(sim, 0.0, cfra, oldtotpart);
 
+               if (psys->fluid_springs) {
+                       MEM_freeN(psys->fluid_springs);
+                       psys->fluid_springs = NULL;
+               }
+
+               psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
+
                /* flag for possible explode modifiers after this system */
                sim->psmd->flag |= eParticleSystemFlag_Pars;
 
@@ -3851,6 +3991,8 @@ static void fluid_default_settings(ParticleSettings *part){
 
        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->viscosity_omega = 2.f;
        fluid->viscosity_beta = 0.f;
index eb0601b413e94c7df2f7698cd1774099c657b228..5408e57f10d299782a384e9bad835df2e3511017 100644 (file)
@@ -390,6 +390,48 @@ static int  ptcache_particle_totwrite(void *psys_v, int cfra)
        return totwrite;
 }
 
+static void ptcache_particle_extra_write(void *psys_v, PTCacheMem *pm, int UNUSED(cfra))
+{
+       ParticleSystem *psys = psys_v;
+       PTCacheExtra *extra = NULL;
+
+       if(psys->part->phystype == PART_PHYS_FLUID &&
+               psys->part->fluid && psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS &&
+               psys->tot_fluidsprings && psys->fluid_springs) {
+
+               extra = MEM_callocN(sizeof(PTCacheExtra), "Point cache: fluid extra data");
+
+               extra->type = BPHYS_EXTRA_FLUID_SPRINGS;
+               extra->totdata = psys->tot_fluidsprings;
+
+               extra->data = MEM_callocN(extra->totdata * ptcache_extra_datasize[extra->type], "Point cache: extra data");
+               memcpy(extra->data, psys->fluid_springs, extra->totdata * ptcache_extra_datasize[extra->type]);
+
+               BLI_addtail(&pm->extradata, extra);
+       }
+}
+
+static int ptcache_particle_extra_read(void *psys_v, PTCacheMem *pm, float UNUSED(cfra))
+{
+       ParticleSystem *psys = psys_v;
+       PTCacheExtra *extra = pm->extradata.first;
+
+       for(; extra; extra=extra->next) {
+               switch(extra->type) {
+                       case BPHYS_EXTRA_FLUID_SPRINGS:
+                       {
+                               if(psys->fluid_springs)
+                                       MEM_freeN(psys->fluid_springs);
+
+                               psys->fluid_springs = MEM_dupallocN(extra->data);
+                               psys->tot_fluidsprings = psys->alloc_fluidsprings = extra->totdata;
+                               break;
+                       }
+               }
+       }
+       return 1;
+}
+
 /* Cloth functions */
 static int  ptcache_cloth_write(int index, void *cloth_v, void **data, int UNUSED(cfra))
 {
@@ -667,6 +709,10 @@ void BKE_ptcache_id_from_particles(PTCacheID *pid, Object *ob, ParticleSystem *p
 
        if(psys->part->phystype == PART_PHYS_BOIDS)
                pid->data_types|= (1<<BPHYS_DATA_AVELOCITY) | (1<<BPHYS_DATA_ROTATION) | (1<<BPHYS_DATA_BOIDS);
+       else if(psys->part->phystype == PART_PHYS_FLUID && psys->part->fluid && psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS) {
+               pid->write_extra_data = ptcache_particle_extra_write;
+               pid->read_extra_data = ptcache_particle_extra_read;
+       }
 
        if(psys->part->rotmode!=PART_ROT_VEL
                || psys->part->avemode!=PART_AVE_SPIN || psys->part->avefac!=0.0f)
@@ -1261,9 +1307,13 @@ static void ptcache_extra_free(PTCacheMem *pm)
 {
        PTCacheExtra *extra = pm->extradata.first;
 
-       for(; extra; extra=extra->next) {
-               if(extra->data)
-                       MEM_freeN(extra->data);
+       if(extra) {
+               for(; extra; extra=extra->next) {
+                       if(extra->data)
+                               MEM_freeN(extra->data);
+               }
+
+               BLI_freelistN(&pm->extradata);
        }
 }
 static int ptcache_old_elemsize(PTCacheID *pid)
@@ -1383,16 +1433,14 @@ static PTCacheMem *ptcache_disk_frame_to_mem(PTCacheID *pid, int cfra)
 
                        extra->type = extratype;
 
-                       ptcache_file_read(pf, &extra->flag, 1, sizeof(unsigned int));
                        ptcache_file_read(pf, &extra->totdata, 1, sizeof(unsigned int));
-                       ptcache_file_read(pf, &extra->datasize, 1, sizeof(unsigned int));
 
-                       extra->data = MEM_callocN(extra->totdata * extra->datasize, "Pointcache extradata->data");
+                       extra->data = MEM_callocN(extra->totdata * ptcache_extra_datasize[extra->type], "Pointcache extradata->data");
 
                        if(pf->flag & PTCACHE_TYPEFLAG_COMPRESS)
-                               ptcache_file_compressed_read(pf, (unsigned char*)(extra->data), extra->totdata*extra->datasize);
+                               ptcache_file_compressed_read(pf, (unsigned char*)(extra->data), extra->totdata*ptcache_extra_datasize[extra->type]);
                        else
-                               ptcache_file_read(pf, extra->data, extra->totdata, extra->datasize);
+                               ptcache_file_read(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
 
                        BLI_addtail(&pm->extradata, extra);
                }
@@ -1475,18 +1523,16 @@ static int ptcache_mem_frame_to_disk(PTCacheID *pid, PTCacheMem *pm)
                                continue;
 
                        ptcache_file_write(pf, &extra->type, 1, sizeof(unsigned int));
-                       ptcache_file_write(pf, &extra->flag, 1, sizeof(unsigned int));
                        ptcache_file_write(pf, &extra->totdata, 1, sizeof(unsigned int));
-                       ptcache_file_write(pf, &extra->datasize, 1, sizeof(unsigned int));
 
                        if(pid->cache->compression) {
-                               unsigned int in_len = extra->totdata * extra->datasize;
+                               unsigned int in_len = extra->totdata * ptcache_extra_datasize[extra->type];
                                unsigned char *out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len)*4, "pointcache_lzo_buffer");
                                ptcache_file_compressed_write(pf, (unsigned char*)(extra->data), in_len, out, pid->cache->compression);
                                MEM_freeN(out);
                        }
                        else {
-                               ptcache_file_write(pf, extra->data, extra->totdata, extra->datasize);
+                               ptcache_file_write(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
                        }
                }
        }
index cc2b73d87f5792a5c83e3abc874834a76fc0492c..a86363cf7ad1b6b74f7e23f718fae71f221a04b8 100644 (file)
@@ -2929,6 +2929,7 @@ static void direct_link_pointcache(FileData *fd, PointCache *cache)
 {
        if((cache->flag & PTCACHE_DISK_CACHE)==0) {
                PTCacheMem *pm;
+               PTCacheExtra *extra;
                int i;
 
                link_list(fd, &cache->mem_cache);
@@ -2948,6 +2949,11 @@ static void direct_link_pointcache(FileData *fd, PointCache *cache)
                                                SWITCH_INT(poin[j]);
                                }
                        }
+                       
+                       link_list(fd, &pm->extradata);
+
+                       for(extra=pm->extradata.first; extra; extra=extra->next)
+                               extra->data = newdataadr(fd, extra->data);
                }
        }
        else
@@ -3156,6 +3162,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles)
                                pa->boid = NULL;
                }
 
+               psys->fluid_springs = newdataadr(fd, psys->fluid_springs);
 
                psys->child = newdataadr(fd,psys->child);
                psys->effectors = NULL;
index 8230050fc2fd0c5336dd2781968b1149f4325594..fd8fb8ad8a6ddb0859b88a7f493b05983c07b269 100644 (file)
@@ -780,6 +780,8 @@ static void write_pointcaches(WriteData *wd, ListBase *ptcaches)
                        PTCacheMem *pm = cache->mem_cache.first;
 
                        for(; pm; pm=pm->next) {
+                               PTCacheExtra *extra = pm->extradata.first;
+
                                writestruct(wd, DATA, "PTCacheMem", 1, pm);
                                
                                for(i=0; i<BPHYS_TOT_DATA; i++) {
@@ -790,6 +792,13 @@ static void write_pointcaches(WriteData *wd, ListBase *ptcaches)
                                                        writestruct(wd, DATA, ptcache_datastruct[i], pm->totpoint, pm->data[i]);
                                        }
                                }
+
+                               for(; extra; extra=extra->next) {
+                                       if(strcmp(ptcache_extra_datastruct[extra->type], "")==0)
+                                               continue;
+                                       writestruct(wd, DATA, "PTCacheExtra", 1, extra);
+                                       writestruct(wd, DATA, ptcache_extra_datastruct[extra->type], extra->totdata, extra->data);
+                               }
                        }
                }
        }
@@ -850,6 +859,9 @@ static void write_particlesystems(WriteData *wd, ListBase *particles)
 
                        if(psys->particles->boid && psys->part->phystype == PART_PHYS_BOIDS)
                                writestruct(wd, DATA, "BoidParticle", psys->totpart, psys->particles->boid);
+
+                       if(psys->part->fluid && psys->part->phystype == PART_PHYS_FLUID && (psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS))
+                               writestruct(wd, DATA, "ParticleSpring", psys->tot_fluidsprings, psys->fluid_springs);
                }
                pt = psys->targets.first;
                for(; pt; pt=pt->next)
index a6898279a1da5919140ee32589edd3964d5035f3..89d31586f6b144cec4f3029eff31007f244d37d2 100644 (file)
@@ -141,10 +141,11 @@ typedef struct EffectorWeights {
 
 #define BPHYS_TOT_DATA                 8
 
+#define BPHYS_EXTRA_FLUID_SPRINGS      1
+
 typedef struct PTCacheExtra {
        struct PTCacheExtra *next, *prev;
-       unsigned int type, flag;
-       unsigned int totdata, datasize;
+       unsigned int type, totdata;
        void *data;
 } PTCacheExtra;
 
index cbc9e0f1c29cc18102ac88ca4599c668c7baa7a1..f140d60d3e9821be0d5bbb69b36c034286913a37 100644 (file)
@@ -61,6 +61,11 @@ typedef struct BoidParticle {
        float rt;
 } BoidParticle;
 
+typedef struct ParticleSpring {
+       float rest_length;
+       unsigned int particle_index[2], delete_flag;
+}ParticleSpring;
+
 /* Child particles are created around or between parent particles */
 typedef struct ChildParticle {
        int num, parent;        /* num is face index on the final derived mesh */
@@ -116,12 +121,17 @@ typedef struct ParticleData {
 
 typedef struct SPHFluidSettings {
        /*Particle Fluid*/
-       float spring_k, radius, rest_length;
+       float spring_k, radius, rest_length, plasticity_constant, yield_ratio;
        float viscosity_omega, viscosity_beta;
        float stiffness_k, stiffness_knear, rest_density;
        float buoyancy;
+       int flag, pad;
 } SPHFluidSettings;
 
+/* fluid->flag */
+#define SPH_VISCOELASTIC_SPRINGS       1
+#define SPH_CURRENT_REST_LENGTH                2
+
 typedef struct ParticleSettings {
        ID id;
        struct AnimData *adt;
@@ -254,6 +264,9 @@ typedef struct ParticleSystem{                              /* note, make sure all (runtime) are NULL's in
 
        struct ListBase *effectors;
 
+       ParticleSpring *fluid_springs;
+       int tot_fluidsprings, alloc_fluidsprings;
+
        struct KDTree *tree;                                    /* used for interactions with self and other systems */
 
        struct ParticleDrawData *pdd;
index cb17893ce0d656f182ba689599e6db42432e919f..aa5f69e71644d833b9b788cb25110583391985f2 100644 (file)
@@ -989,59 +989,88 @@ static void rna_def_fluid_settings(BlenderRNA *brna)
        /* Fluid settings */
        prop= RNA_def_property(srna, "spring_force", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "spring_k");
-       RNA_def_property_range(prop, 0.0f, 1.0f);
-       RNA_def_property_ui_text(prop, "Spring", "Spring force constant");
+       RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Spring Force", "Spring force");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
   
        prop= RNA_def_property(srna, "fluid_radius", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "radius");
-       RNA_def_property_range(prop, 0.0f, 2.0f);
-       RNA_def_property_ui_text(prop, "Radius", "Fluid interaction Radius");
+       RNA_def_property_range(prop, 0.0f, 20.0f);
+       RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Interaction Radius", "Fluid interaction radius");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
 
+       /* Hidden in ui to give a little easier user experience. */
        prop= RNA_def_property(srna, "rest_length", PROP_FLOAT, PROP_NONE);
-       RNA_def_property_float_sdna(prop, NULL, "rest_length");
        RNA_def_property_range(prop, 0.0f, 1.0f);
-       RNA_def_property_ui_text(prop, "Rest Length", "The Spring Rest Length (factor of interaction radius)");
-       RNA_def_property_update(prop, 0, "rna_Particle_reset"); 
+       RNA_def_property_ui_text(prop, "Rest Length", "Spring rest length (factor of interaction radius)");
+       RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+       prop= RNA_def_property(srna, "use_viscoelastic_springs", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_VISCOELASTIC_SPRINGS);
+       RNA_def_property_ui_text(prop, "Viscoelastic Springs", "Use viscoelastic springs instead of Hooke's springs");
+       RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+       prop= RNA_def_property(srna, "use_initial_rest_length", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_CURRENT_REST_LENGTH);
+       RNA_def_property_ui_text(prop, "Initial Rest Length", "Use the initial length as spring rest length instead of interaction radius/2");
+       RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+       prop= RNA_def_property(srna, "plasticity", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "plasticity_constant");
+       RNA_def_property_range(prop, 0.0f, 1.0f);
+       RNA_def_property_ui_text(prop, "Plasticity", "How much the spring rest length can change after the elastic limit is crossed");
+       RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+       prop= RNA_def_property(srna, "yield_ratio", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "yield_ratio");
+       RNA_def_property_range(prop, 0.0f, 1.0f);
+       RNA_def_property_ui_text(prop, "Elastic Limit", "How much the spring has to be stretched/compressed in order to change it's rest length");
+       RNA_def_property_update(prop, 0, "rna_Particle_reset");
  
        /* Viscosity */
-       prop= RNA_def_property(srna, "viscosity_omega", PROP_FLOAT, PROP_NONE);
+       prop= RNA_def_property(srna, "linear_viscosity", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "viscosity_omega");
        RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
        RNA_def_property_ui_text(prop, "Viscosity", "Linear viscosity");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
   
-       prop= RNA_def_property(srna, "viscosity_beta", PROP_FLOAT, PROP_NONE);
+       prop= RNA_def_property(srna, "square_viscosity", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "viscosity_beta");
        RNA_def_property_range(prop, 0.0f, 100.0f);
-       RNA_def_property_ui_text(prop, "Square viscosity", "Square viscosity factor");
+       RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Square viscosity", "Square viscosity");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
 
        /* Double density relaxation */
-       prop= RNA_def_property(srna, "stiffness", PROP_FLOAT, PROP_NONE);
+       prop= RNA_def_property(srna, "density_force", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "stiffness_k");
        RNA_def_property_range(prop, 0.0f, 100.0f);
-       RNA_def_property_ui_text(prop, "Stiffness ", "Constant K - Stiffness");
+       RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Density Force", "How strongly the fluid tends to rest density");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
   
-       prop= RNA_def_property(srna, "stiffness_near", PROP_FLOAT, PROP_NONE);
+       prop= RNA_def_property(srna, "repulsion_force", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "stiffness_knear");
        RNA_def_property_range(prop, 0.0f, 100.0f);
-       RNA_def_property_ui_text(prop, "Repulsion", "Repulsion factor: stiffness_knear");
+       RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Repulsion", "How strongly the fluid tries to keep from clustering");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
   
        prop= RNA_def_property(srna, "rest_density", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "rest_density");
-       RNA_def_property_range(prop, 0.0f, 100.0f);
-       RNA_def_property_ui_text(prop, "Rest Density", "Density");
+       RNA_def_property_range(prop, 0.0f, 1000.0f);
+       RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3);
+       RNA_def_property_ui_text(prop, "Rest Density", "Rest density of the fluid");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
        
        /* Buoyancy */
        prop= RNA_def_property(srna, "buoyancy", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "buoyancy");
        RNA_def_property_range(prop, 0.0f, 1.0f);
-       RNA_def_property_ui_text(prop, "Buoyancy", "");
+       RNA_def_property_ui_text(prop, "Buoyancy", "Artificial buoyancy force in negative gravity direction based on pressure differences inside the fluid");
        RNA_def_property_update(prop, 0, "rna_Particle_reset");
        
 }