Smoke simulator: Add flow subframes and ability to set custom particle size.

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.
This commit is contained in:
Miika Hamalainen 2013-05-17 17:45:37 +00:00
parent c70a900736
commit d3a6c0c834
5 changed files with 471 additions and 60 deletions

View 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

View 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"
@ -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;
static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, SmokeFlowSettings *sfs, EmissionMap *em, Scene *scene, float time, float dt)
/* 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 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++)
{
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))
if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) {
for (p = 0; p < valid_particles; p++)
{
VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi);
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 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++) {
min[i] = em->min[i] * hires_multiplier;
max[i] = em->max[i] * hires_multiplier;
res[i] = em->res[i] * hires_multiplier;
}
} // particles loop
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);
}
}
}
}
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);
}

View 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! */

View 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;

View 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",