Smoke simulator: Add flow subframes and ability to set custom particle size.
authorMiika Hamalainen <blender@miikah.org>
Fri, 17 May 2013 17:45:37 +0000 (17:45 +0000)
committerMiika Hamalainen <blender@miikah.org>
Fri, 17 May 2013 17:45:37 +0000 (17:45 +0000)
Previously it was nearly impossible to have fast moving objects emitting smoke or they would just leave behind a row of smoke poofs instead of continious stream of smoke. Now it's possible to set number of subframes for each smoke flow.

Another new thing is ability to set size of smoke flow particles instead of using closest smoke cell. This also works with my earlier "full sample" commit, so no more blocky particles either. :)

For more info check my blog post: http://www.miikahweb.com/en/blog/2013/05/17/blender-smoke-subframes

This commit also includes couple of fixes I spotted while testing:
* Fix: dissolve was applied at different time for low res and high res simulations.
* Fix: full sample setting didn't get copied with domain.

release/scripts/startup/bl_ui/properties_physics_smoke.py
source/blender/blenkernel/intern/smoke.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_smoke_types.h
source/blender/makesrna/intern/rna_smoke.c

index 842497fc74d0f772b6adeb920b6148cbd16f9be4..0651228388e067339e9a95d454037569ec27c2e4 100644 (file)
@@ -87,6 +87,10 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
                 if flow.smoke_flow_source == "PARTICLES":
                     col.label(text="Particle System:")
                     col.prop_search(flow, "particle_system", ob, "particle_systems", text="")
+                    col.prop(flow, "use_particle_size", text="Set Size")
+                    sub = col.column()
+                    sub.active = flow.use_particle_size
+                    sub.prop(flow, "particle_size")
                 else:
                     col.prop(flow, "surface_distance")
                     col.prop(flow, "volume_density")
@@ -110,6 +114,8 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
                     sub.prop(flow, "smoke_color")
                 if flow.smoke_flow_type in {'FIRE', 'BOTH'}:
                     sub.prop(flow, "fuel_amount")
+                sub.label(text="Sampling:")
+                sub.prop(flow, "subframes")
 
         elif md.smoke_type == 'COLLISION':
             coll = md.coll_settings
index 1c66c5ccf77677674374a09a5473b29a26ada581..6ccbfe9378b76059caab1829442fe5ef65ff97d6 100644 (file)
@@ -54,6 +54,9 @@
 #include "BLI_utildefines.h"
 #include "BLI_voxel.h"
 
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
 #include "DNA_customdata_types.h"
 #include "DNA_group_types.h"
 #include "DNA_lamp_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_smoke_types.h"
 
+#include "BKE_animsys.h"
+#include "BKE_armature.h"
 #include "BKE_bvhutils.h"
 #include "BKE_cdderivedmesh.h"
 #include "BKE_collision.h"
+#include "BKE_constraint.h"
 #include "BKE_customdata.h"
 #include "BKE_deform.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_effect.h"
 #include "BKE_modifier.h"
+#include "BKE_object.h"
 #include "BKE_particle.h"
 #include "BKE_pointcache.h"
+#include "BKE_scene.h"
 #include "BKE_smoke.h"
 
 #include "RE_shader_ext.h"
@@ -561,11 +569,14 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
                        smd->flow->density = 1.0f;
                        smd->flow->fuel_amount = 1.0f;
                        smd->flow->temp = 1.0f;
-                       smd->flow->flags = MOD_SMOKE_FLOW_ABSOLUTE;
+                       smd->flow->flags = MOD_SMOKE_FLOW_ABSOLUTE | MOD_SMOKE_FLOW_USE_PART_SIZE;
                        smd->flow->vel_multi = 1.0f;
+                       smd->flow->volume_density = 0.0f;
                        smd->flow->surface_distance = 1.5f;
                        smd->flow->source = MOD_SMOKE_FLOW_SOURCE_MESH;
                        smd->flow->texture_size = 1.0f;
+                       smd->flow->particle_size = 1.0f;
+                       smd->flow->subframes = 0;
 
                        smd->flow->color[0] = 0.7f;
                        smd->flow->color[1] = 0.7f;
@@ -615,6 +626,7 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
                tsmd->domain->amplify = smd->domain->amplify;
                tsmd->domain->maxres = smd->domain->maxres;
                tsmd->domain->flags = smd->domain->flags;
+               tsmd->domain->highres_sampling = smd->domain->highres_sampling;
                tsmd->domain->viewsettings = smd->domain->viewsettings;
                tsmd->domain->noise = smd->domain->noise;
                tsmd->domain->diss_speed = smd->domain->diss_speed;
@@ -648,6 +660,8 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
                tsmd->flow->temp = smd->flow->temp;
                tsmd->flow->volume_density = smd->flow->volume_density;
                tsmd->flow->surface_distance = smd->flow->surface_distance;
+               tsmd->flow->particle_size = smd->flow->particle_size;
+               tsmd->flow->subframes = smd->flow->subframes;
 
                tsmd->flow->texture_size = smd->flow->texture_size;
                tsmd->flow->texture_offset = smd->flow->texture_offset;
@@ -694,6 +708,10 @@ static int get_lamp(Scene *scene, float *light)
        return found_lamp;
 }
 
+/**********************************************************
+ *     Obstacles
+ **********************************************************/
+
 static void obstacles_from_derivedmesh(Object *coll_ob, SmokeDomainSettings *sds, SmokeCollSettings *scs, unsigned char *obstacle_map, float *velocityX, float *velocityY, float *velocityZ, float dt)
 {
        if (!scs->dm) return;
@@ -898,11 +916,107 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds,
 }
 
 
+/**********************************************************
+ *     Object subframe update method from dynamicpaint.c
+ **********************************************************/
+
+/* set "ignore cache" flag for all caches on this object */
+static void object_cacheIgnoreClear(Object *ob, int state)
+{
+       ListBase pidlist;
+       PTCacheID *pid;
+       BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+       for (pid = pidlist.first; pid; pid = pid->next) {
+               if (pid->cache) {
+                       if (state)
+                               pid->cache->flag |= PTCACHE_IGNORE_CLEAR;
+                       else
+                               pid->cache->flag &= ~PTCACHE_IGNORE_CLEAR;
+               }
+       }
+
+       BLI_freelistN(&pidlist);
+}
+
+static int subframe_updateObject(Scene *scene, Object *ob, int update_mesh, int parent_recursion, float frame)
+{
+       SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+       bConstraint *con;
+
+       /* if other is dynamic paint canvas, don't update */
+       if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN))
+               return 1;
+
+       /* if object has parents, update them too */
+       if (parent_recursion) {
+               int recursion = parent_recursion - 1;
+               int is_domain = 0;
+               if (ob->parent) is_domain += subframe_updateObject(scene, ob->parent, 0, recursion, frame);
+               if (ob->track) is_domain += subframe_updateObject(scene, ob->track, 0, recursion, frame);
+
+               /* skip subframe if object is parented
+                *  to vertex of a dynamic paint canvas */
+               if (is_domain && (ob->partype == PARVERT1 || ob->partype == PARVERT3))
+                       return 0;
+
+               /* also update constraint targets */
+               for (con = ob->constraints.first; con; con = con->next) {
+                       bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
+                       ListBase targets = {NULL, NULL};
+
+                       if (cti && cti->get_constraint_targets) {
+                               bConstraintTarget *ct;
+                               cti->get_constraint_targets(con, &targets);
+                               for (ct = targets.first; ct; ct = ct->next) {
+                                       if (ct->tar)
+                                               subframe_updateObject(scene, ct->tar, 0, recursion, frame);
+                               }
+                               /* free temp targets */
+                               if (cti->flush_constraint_targets)
+                                       cti->flush_constraint_targets(con, &targets, 0);
+                       }
+               }
+       }
+
+       /* was originally OB_RECALC_ALL - TODO - which flags are really needed??? */
+       ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
+       BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, frame, ADT_RECALC_ANIM);
+       if (update_mesh) {
+               /* ignore cache clear during subframe updates
+                *  to not mess up cache validity */
+               object_cacheIgnoreClear(ob, 1);
+               BKE_object_handle_update(scene, ob);
+               object_cacheIgnoreClear(ob, 0);
+       }
+       else
+               BKE_object_where_is_calc_time(scene, ob, frame);
+
+       /* for curve following objects, parented curve has to be updated too */
+       if (ob->type == OB_CURVE) {
+               Curve *cu = ob->data;
+               BKE_animsys_evaluate_animdata(scene, &cu->id, cu->adt, frame, ADT_RECALC_ANIM);
+       }
+       /* and armatures... */
+       if (ob->type == OB_ARMATURE) {
+               bArmature *arm = ob->data;
+               BKE_animsys_evaluate_animdata(scene, &arm->id, arm->adt, frame, ADT_RECALC_ANIM);
+               BKE_pose_where_is(scene, ob);
+       }
+
+       return 0;
+}
+
+/**********************************************************
+ *     Flow emission code
+ **********************************************************/
+
 typedef struct EmissionMap {
        float *influence;
        float *influence_high;
        float *velocity;
        int min[3], max[3], res[3];
+       int hmin[3], hmax[3], hres[3];
        int total_cells, valid;
 } EmissionMap;
 
@@ -935,7 +1049,7 @@ static void clampBoundsInDomain(SmokeDomainSettings *sds, int min[3], int max[3]
 
                /* adapt to velocity */
                if (min_vel && min_vel[i] < 0.0f) {
-                       min[i] += (int)ceil(min_vel[i] * dt);
+                       min[i] += (int)floor(min_vel[i] * dt);
                }
                if (max_vel && max_vel[i] > 0.0f) {
                        max[i] += (int)ceil(max_vel[i] * dt);
@@ -967,8 +1081,16 @@ static void em_allocateData(EmissionMap *em, int use_velocity, int hires_mul)
        /* allocate high resolution map if required */
        if (hires_mul > 1) {
                int total_cells_high = em->total_cells * (hires_mul * hires_mul * hires_mul);
+
+               for (i = 0; i < 3; i++) {
+                       em->hmin[i] = em->min[i] * hires_mul;
+                       em->hmax[i] = em->max[i] * hires_mul;
+                       em->hres[i] = em->res[i] * hires_mul;
+               }
+
                em->influence_high = MEM_callocN(sizeof(float) * total_cells_high, "smoke_flow_influence_high");
        }
+       em->valid = 1;
 }
 
 static void em_freeData(EmissionMap *em)
@@ -981,8 +1103,108 @@ static void em_freeData(EmissionMap *em)
                MEM_freeN(em->velocity);
 }
 
+static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_multiplier, int additive, float sample_size)
+{
+       int i, x,y,z;
+
+       /* copyfill input 1 struct and clear output for new allocation */
+       EmissionMap em1;
+       memcpy(&em1, output, sizeof(EmissionMap));
+       memset(output, 0, sizeof(EmissionMap));
+
+       for (i = 0; i < 3; i++) {
+               if (em1.valid) {
+                       output->min[i] = MIN2(em1.min[i], em2->min[i]);
+                       output->max[i] = MAX2(em1.max[i], em2->max[i]);
+               }
+               else {
+                       output->min[i] = em2->min[i];
+                       output->max[i] = em2->max[i];
+               }
+       }
+       /* allocate output map */
+       em_allocateData(output, (em1.velocity || em2->velocity), hires_multiplier);
+
+       /* base resolution inputs */
+       for (x = output->min[0]; x < output->max[0]; x++)
+               for (y = output->min[1]; y < output->max[1]; y++)
+                       for (z = output->min[2]; z < output->max[2]; z++) {
+                               int index_out = smoke_get_index(x - output->min[0], output->res[0], y - output->min[1], output->res[1], z - output->min[2]);
+
+                               /* initialize with first input if in range */
+                               if (x >= em1.min[0] && x < em1.max[0] &&
+                                       y >= em1.min[1] && y < em1.max[1] &&
+                                       z >= em1.min[2] && z < em1.max[2]) {
+                                       int index_in = smoke_get_index(x - em1.min[0], em1.res[0], y - em1.min[1], em1.res[1], z - em1.min[2]);
+
+                                       /* values */
+                                       output->influence[index_out] = em1.influence[index_in];
+                                       if (output->velocity) {
+                                               output->velocity[index_out] = em1.velocity[index_in];
+                                       }
+                               }
+
+                               /* apply second input if in range */
+                               if (x >= em2->min[0] && x < em2->max[0] &&
+                                       y >= em2->min[1] && y < em2->max[1] &&
+                                       z >= em2->min[2] && z < em2->max[2]) {
+                                       int index_in = smoke_get_index(x - em2->min[0], em2->res[0], y - em2->min[1], em2->res[1], z - em2->min[2]);
+
+                                       /* values */
+                                       if (additive) {
+                                               output->influence[index_out] += em2->influence[index_in] * sample_size;
+                                       }
+                                       else {
+                                               output->influence[index_out] = MAX2(em2->influence[index_in], output->influence[index_out]);
+                                       }
+                                       if (output->velocity) {
+                                               /* last sample replaces the velocity */
+                                               output->velocity[index_out] = ADD_IF_LOWER(output->velocity[index_out], em2->velocity[index_in]);
+                                       }
+                               }
+       } // low res loop
+
+
+
+       /* initialize high resolution input if available */
+       if (output->influence_high) {
+               for (x = output->hmin[0]; x < output->hmax[0]; x++)
+                       for (y = output->hmin[1]; y < output->hmax[1]; y++)
+                               for (z = output->hmin[2]; z < output->hmax[2]; z++) {
+                                       int index_out = smoke_get_index(x - output->hmin[0], output->hres[0], y - output->hmin[1], output->hres[1], z - output->hmin[2]);
+
+                                       /* initialize with first input if in range */
+                                       if (x >= em1.hmin[0] && x < em1.hmax[0] &&
+                                               y >= em1.hmin[1] && y < em1.hmax[1] &&
+                                               z >= em1.hmin[2] && z < em1.hmax[2]) {
+                                               int index_in = smoke_get_index(x - em1.hmin[0], em1.hres[0], y - em1.hmin[1], em1.hres[1], z - em1.hmin[2]);
+                                               /* values */
+                                               output->influence_high[index_out] = em1.influence_high[index_in];
+                                       }
+
+                                       /* apply second input if in range */
+                                       if (x >= em2->hmin[0] && x < em2->hmax[0] &&
+                                               y >= em2->hmin[1] && y < em2->hmax[1] &&
+                                               z >= em2->hmin[2] && z < em2->hmax[2]) {
+                                               int index_in = smoke_get_index(x - em2->hmin[0], em2->hres[0], y - em2->hmin[1], em2->hres[1], z - em2->hmin[2]);
+
+                                               /* values */
+                                               if (additive) {
+                                                       output->influence_high[index_out] += em2->influence_high[index_in] * sample_size;
+                                               }
+                                               else {
+                                                       output->influence_high[index_out] = MAX2(em2->influence_high[index_in], output->influence_high[index_out]);
+                                               }
+                                       }
+               } // high res loop
+       }
+
+       /* free original data */
+       em_freeData(&em1);
+}
+
 
-static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, SmokeFlowSettings *sfs, EmissionMap *em, Scene *scene, float time, float dt)
+static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, SmokeFlowSettings *sfs, EmissionMap *em, Scene *scene, float dt)
 {
        if (sfs && sfs->psys && sfs->psys->part && ELEM(sfs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected
        {
@@ -993,23 +1215,44 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
                int totpart = psys->totpart, totchild;
                int p = 0;
                int valid_particles = 0;
+               int bounds_margin = 1;
+
+               /* radius based flow */
+               float solid = sfs->particle_size * 0.5f;
+               float smooth = 0.5f; /* add 0.5 cells of linear falloff to reduce aliasing */
+               int hires_multiplier = 1;
+               int i,z;
+               KDTree *tree;
 
                sim.scene = scene;
                sim.ob = flow_ob;
                sim.psys = psys;
                sim.rng = BLI_rng_new(psys->seed);
 
-               if (psys->part->type == PART_HAIR)
-               {
+               /* initialize particle cache */
+               if (psys->part->type == PART_HAIR) {
                        // TODO: PART_HAIR not supported whatsoever
                        totchild = 0;
                }
-               else
+               else {
                        totchild = psys->totchild * psys->part->disp / 100;
+               }
 
                particle_pos = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles");
                particle_vel = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles");
 
+               /* setup particle radius emission if enabled */
+               if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE) {
+                       tree = BLI_kdtree_new(psys->totpart);
+
+                       /* check need for high resolution map */
+                       if ((sds->flags & MOD_SMOKE_HIGHRES) && (sds->highres_sampling == SM_HRES_FULLSAMPLE)) {
+                               hires_multiplier = sds->amplify + 1;
+                       }
+
+                       bounds_margin = (int)ceil(solid + smooth);
+               }
+
                /* calculate local position for each particle */
                for (p = 0; p < totpart + totchild; p++)
                {
@@ -1026,7 +1269,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
                                        continue;
                        }
 
-                       state.time = time;
+                       state.time = BKE_scene_frame_get(scene); /* use scene time */
                        if (psys_get_particle_state(&sim, p, &state, 0) == 0)
                                continue;
 
@@ -1039,45 +1282,121 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
                        copy_v3_v3(&particle_vel[valid_particles * 3], state.vel);
                        mul_mat3_m4_v3(sds->imat, &particle_vel[valid_particles * 3]);
 
+                       if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE) {
+                               BLI_kdtree_insert(tree, p, pos, NULL);
+                       }
+
                        /* calculate emission map bounds */
                        em_boundInsert(em, pos);
                        valid_particles++;
                }
 
                /* set emission map */
-               clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, 1, dt);
-               em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, 0);
+               clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, bounds_margin, dt);
+               em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier);
 
-               for (p = 0; p < valid_particles; p++)
+               if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) {
+                       for (p = 0; p < valid_particles; p++)
+                       {
+                               int cell[3];
+                               size_t i = 0;
+                               size_t index = 0;
+                               int badcell = 0;
+
+                               /* 1. get corresponding cell */
+                               cell[0] = floor(particle_pos[p * 3]) - em->min[0];
+                               cell[1] = floor(particle_pos[p * 3 + 1]) - em->min[1];
+                               cell[2] = floor(particle_pos[p * 3 + 2]) - em->min[2];
+                               /* check if cell is valid (in the domain boundary) */
+                               for (i = 0; i < 3; i++) {
+                                       if ((cell[i] > em->res[i] - 1) || (cell[i] < 0)) {
+                                               badcell = 1;
+                                               break;
+                                       }
+                               }
+                               if (badcell)
+                                       continue;
+                               /* get cell index */
+                               index = smoke_get_index(cell[0], em->res[0], cell[1], em->res[1], cell[2]);
+                               /* Add influence to emission map */
+                               em->influence[index] = 1.0f;
+                               /* Uses particle velocity as initial velocity for smoke */
+                               if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO))
+                               {
+                                       VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi);
+                               }
+                       }   // particles loop
+               }
+               else // MOD_SMOKE_FLOW_USE_PART_SIZE
                {
-                       int cell[3];
-                       size_t i = 0;
-                       size_t index = 0;
-                       int badcell = 0;
-
-                       /* 1. get corresponding cell */
-                       cell[0] = floor(particle_pos[p * 3]) - em->min[0];
-                       cell[1] = floor(particle_pos[p * 3 + 1]) - em->min[1];
-                       cell[2] = floor(particle_pos[p * 3 + 2]) - em->min[2];
-                       /* check if cell is valid (in the domain boundary) */
+                       int min[3], max[3], res[3];
+                       float hr = 1.0f / ((float)hires_multiplier);
+                       /* slightly adjust high res antialias smoothness based on number of divisions
+                        * to allow smaller details but yet not differing too much from the low res size */
+                       float hr_smooth = smooth * pow(hr, 1.0f/3.0f);
+
+                       /* setup loop bounds */
                        for (i = 0; i < 3; i++) {
-                               if ((cell[i] > em->res[i] - 1) || (cell[i] < 0)) {
-                                       badcell = 1;
-                                       break;
-                               }
+                               min[i] = em->min[i] * hires_multiplier;
+                               max[i] = em->max[i] * hires_multiplier;
+                               res[i] = em->res[i] * hires_multiplier;
                        }
-                       if (badcell)
-                               continue;
-                       /* get cell index */
-                       index = smoke_get_index(cell[0], em->res[0], cell[1], em->res[1], cell[2]);
-                       /* Add influence to emission map */
-                       em->influence[index] = 1.0f;
-                       /* Uses particle velocity as initial velocity for smoke */
-                       if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO))
-                       {
-                               VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi);
+
+                       BLI_kdtree_balance(tree);
+
+                       /* begin thread safe malloc */
+                       BLI_begin_threaded_malloc();
+
+                       #pragma omp parallel for schedule(static)
+                       for (z = min[2]; z < max[2]; z++) {
+                               int x, y;
+                               for (x = min[0]; x < max[0]; x++)
+                                       for (y = min[1]; y < max[1]; y++) {
+                                               /* take low res samples where possible */
+                                               if (hires_multiplier <= 1 || !(x % hires_multiplier || y % hires_multiplier || z % hires_multiplier)) {
+                                                       /* get low res space coordinates */
+                                                       int lx = x / hires_multiplier;
+                                                       int ly = y / hires_multiplier;
+                                                       int lz = z / hires_multiplier;
+
+                                                       int index = smoke_get_index(lx - em->min[0], em->res[0], ly - em->min[1], em->res[1], lz - em->min[2]);
+                                                       float ray_start[3] = {((float)lx) + 0.5f, ((float)ly) + 0.5f, ((float)lz) + 0.5f};
+
+                                                       /* find particle distance from the kdtree */
+                                                       KDTreeNearest nearest;
+                                                       float range = solid + smooth;
+                                                       BLI_kdtree_find_nearest(tree, ray_start, NULL, &nearest);
+
+                                                       if (nearest.dist < range) {
+                                                               em->influence[index] = (nearest.dist < solid) ? 1.0f : (1.0f - (nearest.dist-solid) / smooth);
+                                                       }
+                                               }
+
+                                               /* take high res samples if required */
+                                               if (hires_multiplier > 1) {
+                                                       /* get low res space coordinates */
+                                                       float lx = ((float)x) * hr;
+                                                       float ly = ((float)y) * hr;
+                                                       float lz = ((float)z) * hr;
+
+                                                       int index = smoke_get_index(x - min[0], res[0], y - min[1], res[1], z - min[2]);
+                                                       float ray_start[3] = {lx + 0.5f*hr, ly + 0.5f*hr, lz + 0.5f*hr};
+
+                                                       /* find particle distance from the kdtree */
+                                                       KDTreeNearest nearest;
+                                                       float range = solid + hr_smooth;
+                                                       BLI_kdtree_find_nearest(tree, ray_start, NULL, &nearest);
+
+                                                       if (nearest.dist < range) {
+                                                               em->influence_high[index] = (nearest.dist < solid) ? 1.0f : (1.0f - (nearest.dist-solid) / smooth);
+                                                       }
+                                               }
+
+                                       }
                        }
-               }   // particles loop
+                       BLI_end_threaded_malloc();
+                       BLI_kdtree_free(tree);
+               }
 
                /* free data */
                if (particle_pos)
@@ -1108,7 +1427,7 @@ static void get_texture_value(Tex *texture, float tex_co[3], TexResult *texres)
        }
 }
 
-static void sample_derived_mesh(SmokeFlowSettings *sfs, MVert *mvert, MTFace *tface, MFace *mface, float *influence_map, float *velocity_map, int index, int base_res[3], float flow_center[3], BVHTreeFromMesh *treeData, float ray_start[3],
+static void sample_derivedmesh(SmokeFlowSettings *sfs, MVert *mvert, MTFace *tface, MFace *mface, float *influence_map, float *velocity_map, int index, int base_res[3], float flow_center[3], BVHTreeFromMesh *treeData, float ray_start[3],
                                                                float *vert_vel, int has_velocity, int defgrp_index, MDeformVert *dvert, float x, float y, float z)
 {
        float ray_dir[3] = {1.0f, 0.0f, 0.0f};
@@ -1336,7 +1655,7 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo
                                                        int index = smoke_get_index(lx - em->min[0], em->res[0], ly - em->min[1], em->res[1], lz - em->min[2]);
                                                        float ray_start[3] = {((float)lx) + 0.5f, ((float)ly) + 0.5f, ((float)lz) + 0.5f};
 
-                                                       sample_derived_mesh(sfs, mvert, tface, mface, em->influence, em->velocity, index, sds->base_res, flow_center, &treeData, ray_start,
+                                                       sample_derivedmesh(sfs, mvert, tface, mface, em->influence, em->velocity, index, sds->base_res, flow_center, &treeData, ray_start,
                                                                                                vert_vel, has_velocity, defgrp_index, dvert, (float)lx, (float)ly, (float)lz);
                                                }
 
@@ -1351,7 +1670,7 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo
                                                        int index = smoke_get_index(x - min[0], res[0], y - min[1], res[1], z - min[2]);
                                                        float ray_start[3] = {lx + 0.5f*hr, ly + 0.5f*hr, lz + 0.5f*hr};
 
-                                                       sample_derived_mesh(sfs, mvert, tface, mface, em->influence_high, NULL, index, sds->base_res, flow_center, &treeData, ray_start,
+                                                       sample_derivedmesh(sfs, mvert, tface, mface, em->influence_high, NULL, index, sds->base_res, flow_center, &treeData, ray_start,
                                                                                                vert_vel, has_velocity, defgrp_index, dvert, lx, ly, lz); /* x,y,z needs to be always lowres */
                                                }
 
@@ -1369,6 +1688,10 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo
        }
 }
 
+/**********************************************************
+ *     Smoke step
+ **********************************************************/
+
 static void adjustDomainResolution(SmokeDomainSettings *sds, int new_shift[3], EmissionMap *emaps, unsigned int numflowobj, float dt)
 {
        int min[3] = {32767, 32767, 32767}, max[3] = {-32767, -32767, -32767}, res[3];
@@ -1658,8 +1981,8 @@ BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value
        float dens_flow = (sfs->type == MOD_SMOKE_FLOW_TYPE_FIRE) ? 0.0f : emission_value * sfs->density;
        float fuel_flow = emission_value * sfs->fuel_amount;
        /* add heat */
-       if (heat) {
-               heat[index] = ADD_IF_LOWER(heat[index], emission_value * sfs->temp);
+       if (heat && emission_value > 0.0f) {
+               heat[index] = ADD_IF_LOWER(heat[index], sfs->temp);
        }
        /* absolute */
        if (absolute_flow) {
@@ -1706,7 +2029,7 @@ BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value
        }
 }
 
-static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sds, float time, float dt)
+static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sds, float dt)
 {
        Object **flowobjs = NULL;
        EmissionMap *emaps = NULL;
@@ -1764,13 +2087,61 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
                {
                        // we got nice flow object
                        SmokeFlowSettings *sfs = smd2->flow;
+                       int subframes = sfs->subframes;
                        EmissionMap *em = &emaps[flowIndex];
 
-                       if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) {
-                               emit_from_particles(collob, sds, sfs, em, scene, time, dt);
+                       /* just sample flow directly to emission map if no subframes */
+                       if (!subframes) {
+                               if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) {
+                                       emit_from_particles(collob, sds, sfs, em, scene, dt);
+                               }
+                               else {
+                                       emit_from_derivedmesh(collob, sds, sfs, em, dt);
+                               }
                        }
+                       /* sample subframes */
                        else {
-                               emit_from_derivedmesh(collob, sds, sfs, em, dt);
+                               int scene_frame = scene->r.cfra;
+                               float scene_subframe = scene->r.subframe;
+                               int subframe;
+                               for (subframe = 0; subframe <= subframes; subframe++) {
+                                       EmissionMap em_temp = {0};
+                                       float sample_size = 1.0f / (float)(subframes+1);
+                                       float prev_frame_pos = sample_size * (float)(subframe+1);
+                                       float sdt = dt * sample_size;
+                                       int hires_multiplier = 1;
+
+                                       if ((sds->flags & MOD_SMOKE_HIGHRES) && (sds->highres_sampling == SM_HRES_FULLSAMPLE)) {
+                                               hires_multiplier = sds->amplify + 1;
+                                       }
+
+                                       /* set scene frame to match previous frame + subframe
+                                        * or use current frame for last sample */
+                                       if (subframe < subframes) {
+                                               scene->r.cfra = scene_frame - 1;
+                                               scene->r.subframe = prev_frame_pos;
+                                       }
+                                       else {
+                                               scene->r.cfra = scene_frame;
+                                               scene->r.subframe = 0.0f;
+                                       }
+
+                                       if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) {
+                                               /* emit_from_particles() updates timestep internally */
+                                               emit_from_particles(collob, sds, sfs, &em_temp, scene, sdt);
+                                       }
+                                       else { /* MOD_SMOKE_FLOW_SOURCE_MESH */
+                                               /* update flow object frame */
+                                               subframe_updateObject(scene, collob, 1, 5, BKE_scene_frame_get(scene));
+
+                                               /* apply flow */
+                                               emit_from_derivedmesh(collob, sds, sfs, &em_temp, sdt);
+                                       }
+
+                                       /* combine emission maps */
+                                       em_combineMaps(em, &em_temp, hires_multiplier, !(sfs->flags & MOD_SMOKE_FLOW_ABSOLUTE), sample_size);
+                                       em_freeData(&em_temp);
+                               }
                        }
 
                        /* update required data fields */
@@ -2140,7 +2511,7 @@ static void step(Scene *scene, Object *ob, SmokeModifierData *smd, DerivedMesh *
        for (substep = 0; substep < totalSubsteps; substep++)
        {
                // calc animated obstacle velocities
-               update_flowsfluids(scene, ob, sds, smd->time, dtSubdiv);
+               update_flowsfluids(scene, ob, sds, dtSubdiv);
                update_obstacles(scene, ob, sds, dtSubdiv, substep, totalSubsteps);
 
                if (sds->total_cells > 1) {
@@ -2336,16 +2707,6 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
 
                /* if on second frame, write cache for first frame */
                if ((int)smd->time == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
-                       // create shadows straight after domain initialization so we get nice shadows for startframe, too
-                       smoke_calc_transparency(sds, scene);
-
-                       if (sds->wt && sds->total_cells > 1)
-                       {
-                               if (sds->flags & MOD_SMOKE_DISSOLVE)
-                                       smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
-                               smoke_turbulence_step(sds->wt, sds->fluid);
-                       }
-
                        BKE_ptcache_write(&pid, startframe);
                }
 
@@ -2358,8 +2719,15 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
                // DG: interesting commenting this line + deactivating loading of noise files
                if (framenr != startframe)
                {
-                       if (sds->flags & MOD_SMOKE_DISSOLVE)
+                       if (sds->flags & MOD_SMOKE_DISSOLVE) {
+                               /* low res dissolve */
                                smoke_dissolve(sds->fluid, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
+                               /* high res dissolve */
+                               if (sds->wt) {
+                                       smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
+                               }
+
+                       }
 
                        step(scene, ob, smd, dm, scene->r.frs_sec / scene->r.frs_sec_base);
                }
@@ -2369,8 +2737,6 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
 
                if (sds->wt)
                {
-                       if (sds->flags & MOD_SMOKE_DISSOLVE)
-                               smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
                        smoke_turbulence_step(sds->wt, sds->fluid);
                }
 
index 5f2aa34f206740394978eabe0918a10e4cacbbf3..812e8df389af7b4e83c7cce20ab5721e6072a1c5 100644 (file)
@@ -9465,6 +9465,25 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                }
        }
 
+
+       {
+               Object *ob;
+
+               for (ob = main->object.first; ob; ob = ob->id.next) {
+                       ModifierData *md;
+                       for (md = ob->modifiers.first; md; md = md->next) {
+                               if (md->type == eModifierType_Smoke) {
+                                       SmokeModifierData *smd = (SmokeModifierData *)md;
+                                       if ((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) {
+                                               if (!smd->flow->particle_size) {
+                                                       smd->flow->particle_size = 1.0f;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
        /* WATCH IT!!!: pointers from libdata have not been converted yet here! */
        /* WATCH IT 2!: Userdef struct init see do_versions_userdef() above! */
 
index 042c43c5b93f66de11e1a1ee4a72e0223754ee16..d01eceb44f0511831bffffc4298c499726a9a0e6 100644 (file)
@@ -166,6 +166,7 @@ typedef struct SmokeDomainSettings {
 #define MOD_SMOKE_FLOW_ABSOLUTE (1<<1) /*old style emission*/
 #define MOD_SMOKE_FLOW_INITVELOCITY (1<<2) /* passes particles speed to the smoke */
 #define MOD_SMOKE_FLOW_TEXTUREEMIT (1<<3) /* use texture to control emission speed */
+#define MOD_SMOKE_FLOW_USE_PART_SIZE (1<<4) /* use specific size for particles instead of closest cell */
 
 typedef struct SmokeFlowSettings {
        struct SmokeModifierData *smd; /* for fast RNA access */
@@ -186,6 +187,8 @@ typedef struct SmokeFlowSettings {
        float temp; /* delta temperature (temp - ambient temp) */
        float volume_density; /* density emitted within mesh volume */
        float surface_distance; /* maximum emission distance from mesh surface */
+       float particle_size;
+       int subframes;
        /* texture control */
        float texture_size;
        float texture_offset;
index 66fd5186fc4e68713f948586f4a7c5be0d00c05b..8b01a785f1ec0c9f2a30c2a40b94234955903b2a 100644 (file)
@@ -559,6 +559,23 @@ static void rna_def_smoke_flow_settings(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Surface", "Maximum distance from mesh surface to emit smoke");
        RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
 
+       prop = RNA_def_property(srna, "particle_size", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.1, 20.0);
+       RNA_def_property_ui_range(prop, 0.5, 5.0, 0.05, 5);
+       RNA_def_property_ui_text(prop, "Size", "Particle size in simulation cells");
+       RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+
+       prop = RNA_def_property(srna, "use_particle_size", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_FLOW_USE_PART_SIZE);
+       RNA_def_property_ui_text(prop, "Set Size", "Set particle size in simulation cells or use nearest cell");
+       RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+
+       prop = RNA_def_property(srna, "subframes", PROP_INT, PROP_NONE);
+       RNA_def_property_range(prop, 0, 50);
+       RNA_def_property_ui_range(prop, 0, 10, 1, -1);
+       RNA_def_property_ui_text(prop, "Subframes", "Number of additional samples to take between frames to improve quality of fast moving flows");
+       RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+
        prop = RNA_def_property(srna, "density_vertex_group", PROP_STRING, PROP_NONE);
        RNA_def_property_string_funcs(prop, "rna_SmokeFlow_density_vgroup_get",
                                      "rna_SmokeFlow_density_vgroup_length",