Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py6
-rw-r--r--source/blender/blenkernel/intern/smoke.c482
-rw-r--r--source/blender/blenloader/intern/readfile.c19
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c17
5 files changed, 469 insertions, 58 deletions
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index 842497fc74d..0651228388e 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -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
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 1c66c5ccf77..6ccbfe9378b 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -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"
@@ -65,16 +68,21 @@
#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);
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 5f2aa34f206..812e8df389a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -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! */
diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h
index 042c43c5b93..d01eceb44f0 100644
--- a/source/blender/makesdna/DNA_smoke_types.h
+++ b/source/blender/makesdna/DNA_smoke_types.h
@@ -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;
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 66fd5186fc4..8b01a785f1e 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -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",