diff options
Diffstat (limited to 'source/blender/blenkernel/intern/smoke.c')
-rw-r--r-- | source/blender/blenkernel/intern/smoke.c | 214 |
1 files changed, 181 insertions, 33 deletions
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index a34aa5009e6..6a0af3ed118 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -66,6 +66,7 @@ #include "BKE_bvhutils.h" #include "BKE_cdderivedmesh.h" #include "BKE_collision.h" +#include "BKE_colortools.h" #include "BKE_constraint.h" #include "BKE_customdata.h" #include "BKE_deform.h" @@ -152,7 +153,7 @@ struct SmokeModifierData; #else /* WITH_SMOKE */ /* Stubs to use when smoke is disabled */ -struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; } +struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors), int UNUSED(use_sim)) { return NULL; } //struct FLUID_3D *smoke_init(int *UNUSED(res), float *UNUSED(dx), float *UNUSED(dtdef), int UNUSED(use_heat), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; } void smoke_free(struct FLUID_3D *UNUSED(fluid)) {} float *smoke_get_density(struct FLUID_3D *UNUSED(fluid)) { return NULL; } @@ -195,6 +196,8 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[ { int use_fire = (sds->active_fields & (SM_ACTIVE_HEAT | SM_ACTIVE_FIRE)); int use_colors = (sds->active_fields & SM_ACTIVE_COLORS); + int use_sim = !((sds->point_cache[0] != NULL) && + (sds->point_cache[0]->flag & (PTCACHE_BAKED|PTCACHE_DISK_CACHE)) == (PTCACHE_BAKED|PTCACHE_DISK_CACHE)); if (free_old && sds->wt) smoke_turbulence_free(sds->wt); @@ -206,7 +209,7 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[ /* smoke_turbulence_init uses non-threadsafe functions from fftw3 lib (like fftw_plan & co). */ BLI_lock_thread(LOCK_FFTW); - sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors); + sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors, use_sim); BLI_unlock_thread(LOCK_FFTW); @@ -266,7 +269,7 @@ static void smoke_set_domain_from_derivedmesh(SmokeDomainSettings *sds, Object * } /* apply object scale */ for (i = 0; i < 3; i++) { - size[i] = fabs(size[i] * ob->size[i]); + size[i] = fabsf(size[i] * ob->size[i]); } copy_v3_v3(sds->global_size, size); copy_v3_v3(sds->dp0, min); @@ -563,6 +566,8 @@ void smokeModifier_createType(struct SmokeModifierData *smd) smd->domain->viewsettings = MOD_SMOKE_VIEW_SHOWBIG; smd->domain->effector_weights = BKE_add_effector_weights(NULL); + + smd->domain->display_thickness = 1.0f; } else if (smd->type & MOD_SMOKE_TYPE_FLOW) { @@ -730,7 +735,8 @@ static void obstacles_from_derivedmesh(Object *coll_ob, SmokeDomainSettings *sds BVHTreeFromMesh treeData = {NULL}; int numverts, i, z; - float surface_distance = 0.6; + /* slightly rounded-up sqrt(3 * (0.5)^2) == max. distance of cell boundary along the diagonal */ + const float surface_distance = 0.867f; float *vert_vel = NULL; int has_velocity = 0; @@ -970,7 +976,7 @@ static bool subframe_updateObject(Scene *scene, Object *ob, int update_mesh, int /* also update constraint targets */ for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; if (cti && cti->get_constraint_targets) { @@ -1025,6 +1031,7 @@ typedef struct EmissionMap { float *velocity; int min[3], max[3], res[3]; int hmin[3], hmax[3], hres[3]; + float *color; int total_cells, valid; } EmissionMap; @@ -1069,7 +1076,7 @@ static void clampBoundsInDomain(SmokeDomainSettings *sds, int min[3], int max[3] } } -static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) +static void em_allocateData(EmissionMap *em, bool use_velocity, bool use_color, int hires_mul) { int i, res[3]; @@ -1085,6 +1092,8 @@ static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) em->influence = MEM_callocN(sizeof(float) * em->total_cells, "smoke_flow_influence"); if (use_velocity) em->velocity = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_velocity"); + if (use_color) + em->color = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_color"); /* allocate high resolution map if required */ if (hires_mul > 1) { @@ -1109,6 +1118,8 @@ static void em_freeData(EmissionMap *em) MEM_freeN(em->influence_high); if (em->velocity) MEM_freeN(em->velocity); + if (em->color) + MEM_freeN(em->color); } static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_multiplier, int additive, float sample_size) @@ -1131,7 +1142,7 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult } } /* allocate output map */ - em_allocateData(output, (em1.velocity || em2->velocity), hires_multiplier); + em_allocateData(output, (em1.velocity || em2->velocity), (em1.color || em2->color), hires_multiplier); /* base resolution inputs */ for (x = output->min[0]; x < output->max[0]; x++) @@ -1150,6 +1161,9 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult if (output->velocity && em1.velocity) { copy_v3_v3(&output->velocity[index_out * 3], &em1.velocity[index_in * 3]); } + if (output->color && em1.color) { + copy_v3_v3(&output->color[index_out * 3], &em1.color[index_in * 3]); + } } /* apply second input if in range */ @@ -1171,6 +1185,13 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult output->velocity[index_out * 3 + 1] = ADD_IF_LOWER(output->velocity[index_out * 3 + 1], em2->velocity[index_in * 3 + 1]); output->velocity[index_out * 3 + 2] = ADD_IF_LOWER(output->velocity[index_out * 3 + 2], em2->velocity[index_in * 3 + 2]); } + if (output->color && em2->color) { + /* mix by influence */ + float f1 = output->influence[index_out]; + float f2 = em2->influence[index_in]; + float f = f1 + f2 > 0.0f ? f1 / (f1 + f2) : 0.5f; + interp_v3_v3v3(&output->color[index_out * 3], &output->color[index_out * 3], &em2->color[index_in * 3], f); + } } } // low res loop @@ -1222,6 +1243,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke ParticleSystem *psys = sfs->psys; float *particle_pos; float *particle_vel; + float *particle_texcol; int totpart = psys->totpart, totchild; int p = 0; int valid_particles = 0; @@ -1238,6 +1260,12 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke sim.ob = flow_ob; sim.psys = psys; + /* prepare curvemapping tables */ + if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve) + curvemapping_changed_all(psys->part->clumpcurve); + if ((psys->part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && psys->part->roughcurve) + curvemapping_changed_all(psys->part->roughcurve); + /* initialize particle cache */ if (psys->part->type == PART_HAIR) { // TODO: PART_HAIR not supported whatsoever @@ -1262,6 +1290,11 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke bounds_margin = (int)ceil(solid + smooth); } + if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR) + particle_texcol = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles"); + else + particle_texcol = NULL; + /* calculate local position for each particle */ for (p = 0; p < totpart + totchild; p++) { @@ -1295,6 +1328,16 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke BLI_kdtree_insert(tree, valid_particles, pos); } + if (particle_texcol) { + if (p < totpart) { + ParticleTexture ptex; + psys_get_texture(&sim, &psys->particles[p], &ptex, PAMAP_COLOR, state.time); + copy_v3_v3(&particle_texcol[valid_particles * 3], ptex.color); + } + else + zero_v3(&particle_texcol[valid_particles * 3]); + } + /* calculate emission map bounds */ em_boundInsert(em, pos); valid_particles++; @@ -1302,7 +1345,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke /* set emission map */ clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, bounds_margin, dt); - em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier); + em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR, hires_multiplier); if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) { for (p = 0; p < valid_particles; p++) @@ -1334,6 +1377,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke { VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi); } + if (particle_texcol && em->color) { + copy_v3_v3(&em->color[index * 3], &particle_texcol[p * 3]); + } } // particles loop } else if (valid_particles > 0) { // MOD_SMOKE_FLOW_USE_PART_SIZE @@ -1383,6 +1429,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke { VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[nearest.index * 3], sfs->vel_multi); } + if (particle_texcol && em->color) { + copy_v3_v3(&em->color[index * 3], &particle_texcol[nearest.index * 3]); + } } } @@ -1420,6 +1469,8 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke MEM_freeN(particle_pos); if (particle_vel) MEM_freeN(particle_vel); + if (particle_texcol) + MEM_freeN(particle_texcol); } } @@ -1633,7 +1684,7 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo /* set emission map */ clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, (int)ceil(sfs->surface_distance), dt); - em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier); + em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, false, hires_multiplier); /* setup loop bounds */ for (i = 0; i < 3; i++) { @@ -1975,7 +2026,7 @@ BLI_INLINE void apply_outflow_fields(int index, float *density, float *heat, flo } } -BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b) +BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, const float *color_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b) { int absolute_flow = (sfs->flags & MOD_SMOKE_FLOW_ABSOLUTE); float dens_old = density[index]; @@ -2012,9 +2063,10 @@ BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value /* set color */ if (color_r && dens_flow) { float total_dens = density[index] / (dens_old + dens_flow); - color_r[index] = (color_r[index] + sfs->color[0] * dens_flow) * total_dens; - color_g[index] = (color_g[index] + sfs->color[1] * dens_flow) * total_dens; - color_b[index] = (color_b[index] + sfs->color[2] * dens_flow) * total_dens; + const float *color = (color_value ? color_value : sfs->color); + color_r[index] = (color_r[index] + color[0] * dens_flow) * total_dens; + color_g[index] = (color_g[index] + color[1] * dens_flow) * total_dens; + color_b[index] = (color_b[index] + color[2] * dens_flow) * total_dens; } /* set fire reaction coordinate */ @@ -2161,7 +2213,10 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd } /* activate color field if flows add smoke with varying colors */ if (sfs->type != MOD_SMOKE_FLOW_TYPE_FIRE && sfs->density) { - if (!(active_fields & SM_ACTIVE_COLOR_SET)) { + if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR) { + active_fields |= SM_ACTIVE_COLORS; + } + else if (!(active_fields & SM_ACTIVE_COLOR_SET)) { copy_v3_v3(sds->active_color, sfs->color); active_fields |= SM_ACTIVE_COLOR_SET; } @@ -2242,6 +2297,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd float *velocity_map = em->velocity; float *emission_map = em->influence; float *emission_map_high = em->influence_high; + float *color_map = em->color; int ii, jj, kk, gx, gy, gz, ex, ey, ez, dx, dy, dz, block_size; size_t e_index, d_index, index_big; @@ -2269,7 +2325,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd apply_outflow_fields(d_index, density, heat, fuel, react, color_r, color_g, color_b); } else { // inflow - apply_inflow_fields(sfs, emission_map[e_index], d_index, density, heat, fuel, react, color_r, color_g, color_b); + apply_inflow_fields(sfs, emission_map[e_index], color_map ? &color_map[e_index * 3] : NULL, d_index, density, heat, fuel, react, color_r, color_g, color_b); /* initial velocity */ if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY) { @@ -2283,6 +2339,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd if (bigdensity) { // neighbor cell emission densities (for high resolution smoke smooth interpolation) float c000, c001, c010, c011, c100, c101, c110, c111; + float col000[3], col001[3], col010[3], col011[3], col100[3], col101[3], col110[3], col111[3]; smoke_turbulence_get_res(sds->wt, bigres); block_size = sds->amplify + 1; // high res block size @@ -2297,14 +2354,67 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd c110 = (ez > 0) ? emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1)] : 0; c111 = emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez)]; // this cell + if (color_map) { + static const float Z[3] = {0.0f, 0.0f, 0.0f}; + + copy_v3_v3(col000, (ex > 0 && ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col001, (ex > 0 && ey > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez) * 3] : Z); + copy_v3_v3(col010, (ex > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col011, (ex > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez) * 3] : Z); + + copy_v3_v3(col100, (ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col101, (ey > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez) * 3] : Z); + copy_v3_v3(col110, (ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col111, &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez) * 3]); // this cell + } + else { + zero_v3(col000); + zero_v3(col001); + zero_v3(col010); + zero_v3(col011); + zero_v3(col100); + zero_v3(col101); + zero_v3(col110); + zero_v3(col111); + } + for (ii = 0; ii < block_size; ii++) for (jj = 0; jj < block_size; jj++) for (kk = 0; kk < block_size; kk++) { - float fx, fy, fz, interpolated_value; + float col[3], interpolated_value, *interpolated_color; int shift_x = 0, shift_y = 0, shift_z = 0; + float w[2][2][2]; + bool do_interpolation = ((!((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) && + !(sds->highres_sampling == SM_HRES_NEAREST)) || + color_map); + + /* weights are used for both density and color, + * so calculate them once in advance + */ + if (do_interpolation) { + /* get relative block position + * for interpolation smoothing */ + float fx = (float)ii / block_size + 0.5f / block_size; + float fy = (float)jj / block_size + 0.5f / block_size; + float fz = (float)kk / block_size + 0.5f / block_size; + float mx = 1.0f - fx; + float my = 1.0f - fy; + float mz = 1.0f - fz; + + w[0][0][0] = mx * my * mz; + w[1][0][0] = fx * my * mz; + w[0][1][0] = mx * fy * mz; + w[1][1][0] = fx * fy * mz; + w[0][0][1] = mx * my * fz; + w[1][0][1] = fx * my * fz; + w[0][1][1] = mx * fy * fz; + w[1][1][1] = fx * fy * fz; + } + else + memset(w, 0, sizeof(float) * 8); /* Use full sample emission map if enabled and available */ if ((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) { @@ -2318,22 +2428,17 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd /* Fall back to interpolated */ else { - /* get relative block position - * for interpolation smoothing */ - fx = (float)ii / block_size + 0.5f / block_size; - fy = (float)jj / block_size + 0.5f / block_size; - fz = (float)kk / block_size + 0.5f / block_size; /* calculate trilinear interpolation */ - interpolated_value = c000 * (1 - fx) * (1 - fy) * (1 - fz) + - c100 * fx * (1 - fy) * (1 - fz) + - c010 * (1 - fx) * fy * (1 - fz) + - c001 * (1 - fx) * (1 - fy) * fz + - c101 * fx * (1 - fy) * fz + - c011 * (1 - fx) * fy * fz + - c110 * fx * fy * (1 - fz) + - c111 * fx * fy * fz; - + interpolated_value = 0.0f; + interpolated_value += c000 * w[0][0][0]; + interpolated_value += c100 * w[1][0][0]; + interpolated_value += c010 * w[0][1][0]; + interpolated_value += c110 * w[1][1][0]; + interpolated_value += c001 * w[0][0][1]; + interpolated_value += c101 * w[1][0][1]; + interpolated_value += c011 * w[0][1][1]; + interpolated_value += c111 * w[1][1][1]; /* add some contrast / sharpness * depending on hi-res block size */ @@ -2347,6 +2452,48 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd shift_y = (dy < 1) ? 0 : block_size / 2; shift_z = (dz < 1) ? 0 : block_size / 2; } + + if (color_map) { + float wcol[2][2][2], totw, invtotw; + + /* colors are zero (black) in zero-emission cells, + * so use weighted average based on density to avoid artifacts! + */ + wcol[0][0][0] = w[0][0][0] * c000; + wcol[1][0][0] = w[1][0][0] * c100; + wcol[0][1][0] = w[0][1][0] * c010; + wcol[1][1][0] = w[1][1][0] * c110; + wcol[0][0][1] = w[0][0][1] * c001; + wcol[1][0][1] = w[1][0][1] * c101; + wcol[0][1][1] = w[0][1][1] * c011; + wcol[1][1][1] = w[1][1][1] * c111; + + totw = wcol[0][0][0] + wcol[1][0][0] + wcol[0][1][0] + wcol[1][1][0] + + wcol[0][0][1] + wcol[1][0][1] + wcol[0][1][1] + wcol[1][1][1]; + invtotw = totw > 0.0f ? 1.0f/totw : 0.0f; + wcol[0][0][0] *= invtotw; + wcol[1][0][0] *= invtotw; + wcol[0][1][0] *= invtotw; + wcol[1][1][0] *= invtotw; + wcol[0][0][1] *= invtotw; + wcol[1][0][1] *= invtotw; + wcol[0][1][1] *= invtotw; + wcol[1][1][1] *= invtotw; + + zero_v3(col); + madd_v3_v3fl(col, col000, wcol[0][0][0]); + madd_v3_v3fl(col, col100, wcol[1][0][0]); + madd_v3_v3fl(col, col010, wcol[0][1][0]); + madd_v3_v3fl(col, col110, wcol[1][1][0]); + madd_v3_v3fl(col, col001, wcol[0][0][1]); + madd_v3_v3fl(col, col101, wcol[1][0][1]); + madd_v3_v3fl(col, col011, wcol[0][1][1]); + madd_v3_v3fl(col, col111, wcol[1][1][1]); + + interpolated_color = col; + } + else + interpolated_color = NULL; /* get shifted index for current high resolution block */ index_big = smoke_get_index(block_size * dx + ii - shift_x, bigres[0], block_size * dy + jj - shift_y, bigres[1], block_size * dz + kk - shift_z); @@ -2357,7 +2504,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd } } else { // inflow - apply_inflow_fields(sfs, interpolated_value, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b); + apply_inflow_fields(sfs, interpolated_value, interpolated_color, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b); } } // hires loop } // bigdensity @@ -2381,7 +2528,7 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, ListBase *effectors; /* make sure smoke flow influence is 0.0f */ sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f; - effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true); + effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights); if (effectors) { @@ -2663,7 +2810,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * int startframe, endframe, framenr; float timescale; - framenr = scene->r.cfra; + framenr = scene->r.cfra - sds->point_cache_offset; //printf("time: %d\n", scene->r.cfra); @@ -2744,6 +2891,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * if (sds->wt) { + smoke_ensure_simulation(sds->fluid, sds->wt); smoke_turbulence_step(sds->wt, sds->fluid); } |