diff options
Diffstat (limited to 'source/blender/blenkernel/intern/smoke.c')
-rw-r--r-- | source/blender/blenkernel/intern/smoke.c | 694 |
1 files changed, 663 insertions, 31 deletions
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 28a2c42546d..c8fc991a645 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -91,6 +91,10 @@ #include "smoke_API.h" +#ifdef WITH_OPENVDB +# include "openvdb_capi.h" +#endif + #ifdef WITH_SMOKE #ifdef _WIN32 @@ -153,7 +157,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; } @@ -196,6 +200,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); @@ -207,7 +213,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); @@ -347,6 +353,9 @@ static int smokeModifier_init(SmokeModifierData *smd, Object *ob, Scene *scene, if (!sds->shadow) sds->shadow = MEM_callocN(sizeof(float) * sds->res[0] * sds->res[1] * sds->res[2], "SmokeDomainShadow"); + sds->density = NULL; + sds->density_high = NULL; + return 1; } else if ((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) @@ -376,6 +385,8 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd) { if (smd->domain) { + OpenVDBCache *cache; + if (smd->domain->shadow) MEM_freeN(smd->domain->shadow); smd->domain->shadow = NULL; @@ -396,6 +407,14 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd) BKE_ptcache_free_list(&(smd->domain->ptcaches[0])); smd->domain->point_cache[0] = NULL; + while ((cache = BLI_pophead(&(smd->domain->vdb_caches)))) { +#ifdef WITH_OPENVDB + OpenVDBWriter_free(cache->writer); + OpenVDBReader_free(cache->reader); +#endif + MEM_freeN(cache); + } + MEM_freeN(smd->domain); smd->domain = NULL; } @@ -564,6 +583,10 @@ 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; + + smd->domain->use_openvdb = false; } else if (smd->type & MOD_SMOKE_TYPE_FLOW) { @@ -1027,6 +1050,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; @@ -1071,7 +1095,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]; @@ -1087,6 +1111,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) { @@ -1111,6 +1137,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) @@ -1133,7 +1161,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++) @@ -1152,6 +1180,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 */ @@ -1173,6 +1204,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 @@ -1224,6 +1262,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; @@ -1270,6 +1309,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++) { @@ -1303,6 +1347,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++; @@ -1310,7 +1364,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++) @@ -1342,6 +1396,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 @@ -1391,6 +1448,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]); + } } } @@ -1428,6 +1488,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); } } @@ -1641,7 +1703,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++) { @@ -1983,7 +2045,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]; @@ -2020,9 +2082,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 */ @@ -2169,7 +2232,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; } @@ -2250,6 +2316,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; @@ -2277,7 +2344,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) { @@ -2291,6 +2358,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 @@ -2305,14 +2373,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) { @@ -2326,22 +2447,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 */ @@ -2355,6 +2471,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); @@ -2365,7 +2523,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 @@ -2389,7 +2547,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) { @@ -2667,11 +2825,12 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * { SmokeDomainSettings *sds = smd->domain; PointCache *cache = NULL; + OpenVDBCache *vdb_cache = NULL; PTCacheID pid; 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); @@ -2703,8 +2862,18 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * return; } + /* try to read from openvdb cache */ +// vdb_cache = BKE_openvdb_get_current_cache(sds); +// if (sds->use_openvdb && vdb_cache) { +// if (vdb_cache->flags & VDB_CACHE_SMOKE_EXPORTED) { +// smokeModifier_OpenVDB_import(smd, scene, ob, vdb_cache); +// smd->time = framenr; +// return; +// } +// } + /* try to read from cache */ - if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) { + if (/*!sds->use_openvdb && */(BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT)) { BKE_ptcache_validate(cache, framenr); smd->time = framenr; return; @@ -2752,6 +2921,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); } @@ -3025,4 +3195,466 @@ int smoke_get_data_flags(SmokeDomainSettings *sds) return flags; } +#ifdef WITH_OPENVDB + +/* Construct matrices which represent the fluid object, for low and high res: + * vs 0 0 0 + * 0 vs 0 0 + * 0 0 vs 0 + * px py pz 1 + * with vs = voxel size, and px, py, pz, the min position of the domain's + * bounding box. + */ +static void compute_fluid_matrices(SmokeDomainSettings *sds) +{ + float bbox_min[3]; + + copy_v3_v3(bbox_min, sds->p0); + + if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) { + bbox_min[0] += (sds->cell_size[0] * (float)sds->res_min[0]); + bbox_min[1] += (sds->cell_size[1] * (float)sds->res_min[1]); + bbox_min[2] += (sds->cell_size[2] * (float)sds->res_min[2]); + add_v3_v3(bbox_min, sds->obj_shift_f); + } + + /* construct low res matrix */ + size_to_mat4(sds->fluidmat, sds->cell_size); + copy_v3_v3(sds->fluidmat[3], bbox_min); + + /* The smoke simulator stores voxels cell-centered, whilst VDB is node + * centered, so we offset the matrix by half a voxel to compensate. */ + madd_v3_v3fl(sds->fluidmat[3], sds->cell_size, 0.5f); + + mul_m4_m4m4(sds->fluidmat, sds->obmat, sds->fluidmat); + + if (sds->wt) { + float voxel_size_high[3]; + /* construct high res matrix */ + mul_v3_v3fl(voxel_size_high, sds->cell_size, 1.0f / (float)(sds->amplify + 1)); + size_to_mat4(sds->fluidmat_wt, voxel_size_high); + copy_v3_v3(sds->fluidmat_wt[3], bbox_min); + + /* Same here, add half a voxel to adjust the position of the fluid. */ + madd_v3_v3fl(sds->fluidmat_wt[3], voxel_size_high, 0.5f); + + mul_m4_m4m4(sds->fluidmat_wt, sds->obmat, sds->fluidmat_wt); + } +} + +static void OpenVDB_read_fluid_settings(SmokeDomainSettings *sds, struct OpenVDBReader *reader) +{ + OpenVDBReader_get_meta_v3_int(reader, "min_resolution", sds->res_min); + OpenVDBReader_get_meta_v3_int(reader, "max_resolution", sds->res_max); + OpenVDBReader_get_meta_v3_int(reader, "base_resolution", sds->base_res); + OpenVDBReader_get_meta_fl(reader, "delta_x", &sds->dx); + OpenVDBReader_get_meta_v3(reader, "min_bbox", sds->p0); + OpenVDBReader_get_meta_v3(reader, "max_bbox", sds->p1); + OpenVDBReader_get_meta_v3(reader, "dp0", sds->dp0); + OpenVDBReader_get_meta_v3_int(reader, "shift", sds->shift); + OpenVDBReader_get_meta_v3(reader, "obj_shift_f", sds->obj_shift_f); + OpenVDBReader_get_meta_v3(reader, "active_color", sds->active_color); + OpenVDBReader_get_meta_mat4(reader, "obmat", sds->obmat); +} + +static void OpenVDB_write_fluid_settings(SmokeDomainSettings *sds, struct OpenVDBWriter *writer) +{ + OpenVDBWriter_add_meta_int(writer, "active_fields", sds->active_fields); + OpenVDBWriter_add_meta_v3_int(writer, "resolution", sds->res); + OpenVDBWriter_add_meta_v3_int(writer, "min_resolution", sds->res_min); + OpenVDBWriter_add_meta_v3_int(writer, "max_resolution", sds->res_max); + OpenVDBWriter_add_meta_v3_int(writer, "base_resolution", sds->base_res); + OpenVDBWriter_add_meta_fl(writer, "delta_x", sds->dx); + OpenVDBWriter_add_meta_v3(writer, "min_bbox", sds->p0); + OpenVDBWriter_add_meta_v3(writer, "max_bbox", sds->p1); + OpenVDBWriter_add_meta_v3(writer, "dp0", sds->dp0); + OpenVDBWriter_add_meta_v3_int(writer, "shift", sds->shift); + OpenVDBWriter_add_meta_v3(writer, "obj_shift_f", sds->obj_shift_f); + OpenVDBWriter_add_meta_v3(writer, "active_color", sds->active_color); + OpenVDBWriter_add_meta_mat4(writer, "obmat", sds->obmat); +} + +void BKE_openvdb_cache_filename(char *r_filename, const char *path, const char *fname, const char *relbase, int frame) +{ + char cachepath[FILE_MAX]; + + BLI_strncpy(cachepath, path, FILE_MAX - 10); + BLI_path_abs(cachepath, relbase); + + if (!BLI_exists(cachepath)) { + BLI_dir_create_recursive(cachepath); + } + + BLI_join_dirfile(r_filename, sizeof(cachepath), cachepath, fname); + BLI_path_suffix(r_filename, FILE_MAX, "", "_"); + BLI_path_frame(r_filename, frame, 4); + BLI_ensure_extension(r_filename, FILE_MAX, ".vdb"); +} + +static void OpenVDB_export_smoke(SmokeDomainSettings *sds, struct OpenVDBWriter *writer) +{ + int fluid_fields = smoke_get_data_flags(sds); + struct OpenVDBFloatGrid *clip_grid = NULL; + + compute_fluid_matrices(sds); + + OpenVDBWriter_add_meta_int(writer, "fluid_fields", fluid_fields); + + if (sds->wt) { + struct OpenVDBFloatGrid *wt_density_grid; + float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b; + + smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw); + + wt_density_grid = OpenVDB_export_grid_fl(writer, "Density High", dens, sds->res_wt, sds->fluidmat_wt, NULL); + clip_grid = wt_density_grid; + + if (fluid_fields & SM_ACTIVE_FIRE) { + OpenVDB_export_grid_fl(writer, "Flame High", flame, sds->res_wt, sds->fluidmat_wt, wt_density_grid); + OpenVDB_export_grid_fl(writer, "Fuel High", fuel, sds->res_wt, sds->fluidmat_wt, wt_density_grid); + OpenVDB_export_grid_fl(writer, "React High", react, sds->res_wt, sds->fluidmat_wt, wt_density_grid); + } + + if (fluid_fields & SM_ACTIVE_COLORS) { + OpenVDB_export_grid_vec(writer, "Color High", r, g, b, sds->res_wt, sds->fluidmat_wt, VEC_INVARIANT, true, wt_density_grid); + } + + OpenVDB_export_grid_vec(writer, "Texture Coordinates", tcu, tcv, tcw, sds->res, sds->fluidmat, VEC_INVARIANT, false, wt_density_grid); + } + + if (sds->fluid) { + struct OpenVDBFloatGrid *density_grid; + float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b; + unsigned char *obstacles; + + smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat, + &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles); + + OpenVDBWriter_add_meta_fl(writer, "dx", dx); + OpenVDBWriter_add_meta_fl(writer, "dt", dt); + + density_grid = OpenVDB_export_grid_fl(writer, "Density", dens, sds->res, sds->fluidmat, NULL); + clip_grid = sds->wt ? clip_grid : density_grid; + + OpenVDB_export_grid_fl(writer, "Shadow", sds->shadow, sds->res, sds->fluidmat, density_grid); + + if (fluid_fields & SM_ACTIVE_HEAT) { + OpenVDB_export_grid_fl(writer, "Heat", heat, sds->res, sds->fluidmat, clip_grid); + OpenVDB_export_grid_fl(writer, "Heat Old", heatold, sds->res, sds->fluidmat, clip_grid); + } + + if (fluid_fields & SM_ACTIVE_FIRE) { + OpenVDB_export_grid_fl(writer, "Flame", flame, sds->res, sds->fluidmat, density_grid); + OpenVDB_export_grid_fl(writer, "Fuel", fuel, sds->res, sds->fluidmat, density_grid); + OpenVDB_export_grid_fl(writer, "React", react, sds->res, sds->fluidmat, density_grid); + } + + if (fluid_fields & SM_ACTIVE_COLORS) { + OpenVDB_export_grid_vec(writer, "Color", r, g, b, sds->res, sds->fluidmat, VEC_INVARIANT, true, density_grid); + } + + OpenVDB_export_grid_vec(writer, "Velocity", vx, vy, vz, sds->res, sds->fluidmat, VEC_CONTRAVARIANT_RELATIVE, false, clip_grid); + //OpenVDB_export_grid_ch(writer, "Obstacles", obstacles, sds->res, sds->fluidmat, NULL); + } +} + +static void OpenVDB_import_smoke(SmokeDomainSettings *sds, struct OpenVDBReader *reader, bool for_display) +{ + int fluid_fields = smoke_get_data_flags(sds); + int active_fields, cache_fields = 0; + int cache_res[3]; + float cache_dx; + bool reallocate = false; + + OpenVDBReader_get_meta_int(reader, "fluid_fields", &cache_fields); + OpenVDBReader_get_meta_int(reader, "active_fields", &active_fields); + OpenVDBReader_get_meta_fl(reader, "dx", &cache_dx); + OpenVDBReader_get_meta_v3_int(reader, "resolution", cache_res); + + /* check if resolution has changed */ + if (sds->res[0] != cache_res[0] || + sds->res[1] != cache_res[1] || + sds->res[2] != cache_res[2]) + { + if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) { + reallocate = true; + } + else { + return; + } + } + + /* check if active fields have changed */ + if ((fluid_fields != cache_fields) || (active_fields != sds->active_fields)) { + reallocate = true; + } + + /* reallocate fluid if needed*/ + if (reallocate) { + sds->active_fields = active_fields | cache_fields; + smoke_reallocate_fluid(sds, cache_dx, cache_res, 1); + sds->dx = cache_dx; + copy_v3_v3_int(sds->res, cache_res); + sds->total_cells = cache_res[0] * cache_res[1] * cache_res[2]; + + if (sds->flags & MOD_SMOKE_HIGHRES) { + smoke_reallocate_highres_fluid(sds, cache_dx, cache_res, 1); + } + } + + if (sds->fluid) { + float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b; + unsigned char *obstacles; + bool for_low_display = for_display && (!sds->wt || !(sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)); + + smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat, + &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles); + + OpenVDBReader_get_meta_fl(reader, "dt", &dt); + + OpenVDB_import_grid_fl(reader, "Shadow", &sds->shadow, sds->res); + + if (for_low_display) { + sds->density = OpenVDB_import_grid_fl(reader, "Density", &dens, sds->res); + } + + if (fluid_fields & SM_ACTIVE_HEAT && !for_low_display) { + OpenVDB_import_grid_fl(reader, "Heat", &heat, sds->res); + OpenVDB_import_grid_fl(reader, "Heat Old", &heatold, sds->res); + } + + if (fluid_fields & SM_ACTIVE_FIRE && for_low_display) { + OpenVDB_import_grid_fl(reader, "Flame", &flame, sds->res); + + if (!for_low_display) { // should be "for_sim_cont" or so + OpenVDB_import_grid_fl(reader, "Fuel", &fuel, sds->res); + OpenVDB_import_grid_fl(reader, "React", &react, sds->res); + } + } + + if (fluid_fields & SM_ACTIVE_COLORS && for_low_display) { + OpenVDB_import_grid_vec(reader, "Color", &r, &g, &b, sds->res); + } + + if (!for_low_display) { + OpenVDB_import_grid_vec(reader, "Velocity", &vx, &vy, &vz, sds->res); + //OpenVDB_import_grid_ch(reader, "Obstacles", &obstacles, sds->res); + } + } + + if (sds->wt) { + float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b; + bool for_wt_display = for_display && (sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG); + + smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw); + + if (for_wt_display) { + sds->density_high = OpenVDB_import_grid_fl(reader, "Density High", &dens, sds->res_wt); + } + + if (fluid_fields & SM_ACTIVE_FIRE && for_wt_display) { + OpenVDB_import_grid_fl(reader, "Flame High", &flame, sds->res_wt); + + if (!for_wt_display) { // should be "for_sim_cont" or so + OpenVDB_import_grid_fl(reader, "Fuel High", &fuel, sds->res_wt); + OpenVDB_import_grid_fl(reader, "React High", &react, sds->res_wt); + } + } + + if (fluid_fields & SM_ACTIVE_COLORS && for_wt_display) { + OpenVDB_import_grid_vec(reader, "Color High", &r, &g, &b, sds->res_wt); + } + + if (!for_wt_display) { + OpenVDB_import_grid_vec(reader, "Texture Coordinates", &tcu, &tcv, &tcw, sds->res); + } + } +} + +void smokeModifier_OpenVDB_export(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm, + update_cb update, void *update_cb_data) +{ + SmokeDomainSettings *sds = smd->domain; + OpenVDBCache *cache; + int orig_frame, fr, cancel = 0; + float progress; + const char *relbase = modifier_path_relbase(ob); + char filename[FILE_MAX]; + + orig_frame = scene->r.cfra; + + cache = BKE_openvdb_get_current_cache(sds); + + if (cache->writer == NULL) { + cache->writer = OpenVDBWriter_create(); + } + + OpenVDBWriter_set_compression(cache->writer, cache->compression); + + for (fr = cache->startframe; fr <= cache->endframe; fr++) { + /* smd->time is overwritten with scene->r.cfra in smokeModifier_process, + * so we can't use it here... */ + scene->r.cfra = fr; + + BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, fr); + + smokeModifier_process(smd, scene, ob, dm, false); + + /* XXX hack: for some reason the smoke sim stores zero matrix for frame 1 */ + { + PTCacheID pid; + int ptcache_start, ptcache_end; + float ptcache_timescale; + BKE_ptcache_id_from_smoke(&pid, ob, smd); + BKE_ptcache_id_time(&pid, scene, fr, &ptcache_start, &ptcache_end, &ptcache_timescale); + + if (fr == ptcache_start) { + unit_m4(sds->obmat); + } + } + + OpenVDB_write_fluid_settings(sds, cache->writer); + OpenVDB_export_smoke(sds, cache->writer); + + progress = (fr - cache->startframe) / (float)cache->endframe; + + OpenVDBWriter_write(cache->writer, filename); + + update(update_cb_data, progress, &cancel); + + if (cancel) { + scene->r.cfra = orig_frame; + return; + } + } + + cache->flags |= VDB_CACHE_SMOKE_EXPORTED; + + scene->r.cfra = orig_frame; +} + +void smokeModifier_OpenVDB_import(SmokeModifierData *smd, Scene *scene, Object *ob, OpenVDBCache *cache) +{ + SmokeDomainSettings *sds = smd->domain; + int startframe, endframe; + char filename[FILE_MAX]; + const char *relbase = modifier_path_relbase(ob); + int ret = OPENVDB_NO_ERROR; + bool for_display = false; + + cache = BKE_openvdb_get_current_cache(sds); + startframe = cache->startframe; + endframe = cache->endframe; + + if (CFRA < startframe || CFRA > endframe) { + return; + } + + if (cache->reader == NULL) { + cache->reader = OpenVDBReader_create(); + } + + for_display = true; + + BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, CFRA); + OpenVDBReader_open(cache->reader, filename); + OpenVDB_read_fluid_settings(sds, cache->reader); + OpenVDB_import_smoke(sds, cache->reader, for_display); + + if (ret == OPENVDB_IO_ERROR) { + /* TODO(kevin): report error "OpenVDB import error, see console for details" */ + return; + } + + if (ret == OPENVDB_KEY_ERROR) { + /* It may happen that some grids are missing on the first frame if the + * simulation hasn't started yet, so it's safe to ignore it. */ + if (CFRA > startframe) { + /* TODO(kevin): report error "OpenVDB import error, see console for details" */ + return; + } + } +} + +void smokeModifier_OpenVDB_update_transform(SmokeModifierData *smd, + Scene *scene, + Object *ob, + update_cb update, + void *update_cb_data) +{ + SmokeDomainSettings *sds = smd->domain; + OpenVDBCache *cache; + int orig_frame, fr, cancel = 0; + float progress; + const char *relbase = modifier_path_relbase(ob); + char filename[FILE_MAX]; + + orig_frame = scene->r.cfra; + + cache = BKE_openvdb_get_current_cache(sds); + + for (fr = cache->startframe; fr <= cache->endframe; fr++) { + /* smd->time is overwritten with scene->r.cfra in smokeModifier_process, + * so we can't use it here... */ + scene->r.cfra = fr; + + BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, fr); + compute_fluid_matrices(sds); + OpenVDB_update_fluid_transform(filename, sds->fluidmat, sds->fluidmat_wt); + + progress = (fr - cache->startframe) / (float)cache->endframe; + + update(update_cb_data, progress, &cancel); + + if (cancel) { + scene->r.cfra = orig_frame; + return; + } + } + + scene->r.cfra = orig_frame; +} + +#endif /* WITH_OPENVDB */ + #endif /* WITH_SMOKE */ + +#if !defined(WITH_SMOKE) || !defined(WITH_OPENVDB) + +void smokeModifier_OpenVDB_export(SmokeModifierData *smd, Scene *scene, Object *ob, + DerivedMesh *dm, update_cb update, void *update_cb_data) +{ + UNUSED_VARS(smd, scene, ob, dm, update, update_cb_data); +} + +void smokeModifier_OpenVDB_import(SmokeModifierData *smd, Scene *scene, Object *ob, OpenVDBCache *cache) +{ + UNUSED_VARS(smd, scene, ob, cache); +} + +void smokeModifier_OpenVDB_update_transform(SmokeModifierData *smd, Scene *scene, + Object *ob, update_cb update, void *update_cb_data) +{ + UNUSED_VARS(smd, scene, ob, update, update_cb_data); +} + +void BKE_openvdb_cache_filename(char *r_filename, const char *path, const char *fname, const char *relbase, int frame) +{ + r_filename[0] = '\0'; + UNUSED_VARS(path, fname, relbase, frame); +} + +#endif + +OpenVDBCache *BKE_openvdb_get_current_cache(SmokeDomainSettings *sds) +{ + OpenVDBCache *cache = sds->vdb_caches.first; + + for (; cache; cache = cache->next) { + if (cache->flags & VDB_CACHE_CURRENT) { + break; + } + } + + return cache; +} |