/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2005 by the Blender Foundation. * All rights reserved. * * Contributor(s): Daniel Dunbar * Ton Roosendaal, * Ben Batt, * Brecht Van Lommel, * Campbell Barton * * ***** END GPL LICENSE BLOCK ***** * */ /** \file blender/modifiers/intern/MOD_fluidsim_util.c * \ingroup modifiers */ #include #include #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_fluidsim_types.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "BKE_fluidsim.h" /* ensure definitions here match */ #ifdef WITH_MOD_FLUID # include "BKE_global.h" #endif #include "BKE_library.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "MOD_fluidsim_util.h" #include "MOD_modifiertypes.h" #include "MEM_guardedalloc.h" // headers for fluidsim bobj meshes #include "LBM_fluidsim.h" void fluidsim_init(FluidsimModifierData *fluidmd) { #ifdef WITH_MOD_FLUID if (fluidmd) { FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings"); fluidmd->fss = fss; if (!fss) return; fss->fmd = fluidmd; fss->type = OB_FLUIDSIM_ENABLE; fss->threads = 0; fss->show_advancedoptions = 0; fss->resolutionxyz = 65; fss->previewresxyz = 45; fss->realsize = 0.5; fss->guiDisplayMode = OB_FSDOM_PREVIEW; fss->renderDisplayMode = OB_FSDOM_FINAL; fss->viscosityValue = 1.0; fss->viscosityExponent = 6; fss->grav[0] = 0.0; fss->grav[1] = 0.0; fss->grav[2] = -9.81; fss->animStart = 0.0; fss->animEnd = 4.0; fss->animRate = 1.0; fss->gstar = 0.005; // used as normgstar fss->maxRefine = -1; /* maxRefine is set according to resolutionxyz during bake */ /* fluid/inflow settings * fss->iniVel --> automatically set to 0 */ modifier_path_init(fss->surfdataPath, sizeof(fss->surfdataPath), OB_FLUIDSIM_SURF_DIR_DEFAULT); /* first init of bounding box */ /* no bounding box needed */ /* todo - reuse default init from elbeem! */ fss->typeFlags = OB_FSBND_PARTSLIP | OB_FSSG_NOOBS; fss->domainNovecgen = 0; fss->volumeInitType = 1; /* volume */ fss->partSlipValue = 0.2; fss->generateTracers = 0; fss->generateParticles = 0.0; fss->surfaceSmoothing = 1.0; fss->surfaceSubdivs = 0.0; fss->particleInfSize = 0.0; fss->particleInfAlpha = 0.0; /* init fluid control settings */ fss->attractforceStrength = 0.2; fss->attractforceRadius = 0.75; fss->velocityforceStrength = 0.2; fss->velocityforceRadius = 0.75; fss->cpsTimeStart = fss->animStart; fss->cpsTimeEnd = fss->animEnd; fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width /* * BAD TODO: this is done in buttons_object.c in the moment * Mesh *mesh = ob->data; * // calculate bounding box * fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); */ fss->meshVelocities = NULL; fss->lastgoodframe = -1; fss->flag |= OB_FLUIDSIM_ACTIVE; } #else (void)fluidmd; /* unused */ #endif return; } void fluidsim_free(FluidsimModifierData *fluidmd) { if (fluidmd && fluidmd->fss) { if (fluidmd->fss->meshVelocities) { MEM_freeN(fluidmd->fss->meshVelocities); } MEM_SAFE_FREE(fluidmd->fss); } return; } #ifdef WITH_MOD_FLUID /* read .bobj.gz file into a fluidsimMesh struct */ static Mesh *fluidsim_read_obj(const char *filename, const MPoly *mp_example) { int wri = 0, i; int gotBytes; gzFile gzf; int numverts = 0, numfaces = 0; Mesh *mesh = NULL; MPoly *mp; MLoop *ml; MVert *mv; short *normals, *no_s; float no[3]; const short mp_mat_nr = mp_example->mat_nr; const char mp_flag = mp_example->flag; /* ------------------------------------------------ * get numverts + numfaces first * ------------------------------------------------ */ gzf = BLI_gzopen(filename, "rb"); if (!gzf) { return NULL; } /* read numverts */ gotBytes = gzread(gzf, &wri, sizeof(wri)); numverts = wri; /* skip verts */ gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1; /* read number of normals */ if (gotBytes) gotBytes = gzread(gzf, &wri, sizeof(wri)); /* skip normals */ gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1; /* get no. of triangles */ if (gotBytes) gotBytes = gzread(gzf, &wri, sizeof(wri)); numfaces = wri; gzclose(gzf); /* ------------------------------------------------ */ if (!numfaces || !numverts || !gotBytes) return NULL; gzf = BLI_gzopen(filename, "rb"); if (!gzf) { return NULL; } mesh = BKE_mesh_new_nomain(numverts, 0, 0, numfaces * 3, numfaces); if (!mesh) { gzclose(gzf); return NULL; } /* read numverts */ gotBytes = gzread(gzf, &wri, sizeof(wri)); /* read vertex position from file */ mv = mesh->mvert; for (i = 0; i < numverts; i++, mv++) gotBytes = gzread(gzf, mv->co, sizeof(float) * 3); /* should be the same as numverts */ gotBytes = gzread(gzf, &wri, sizeof(wri)); if (wri != numverts) { if (mesh) BKE_id_free(NULL, mesh); gzclose(gzf); return NULL; } normals = MEM_calloc_arrayN(numverts, 3 * sizeof(short), "fluid_tmp_normals"); if (!normals) { if (mesh) BKE_id_free(NULL, mesh); gzclose(gzf); return NULL; } /* read normals from file (but don't save them yet) */ for (i = numverts, no_s = normals; i > 0; i--, no_s += 3) { gotBytes = gzread(gzf, no, sizeof(float) * 3); normal_float_to_short_v3(no_s, no); } /* read no. of triangles */ gotBytes = gzread(gzf, &wri, sizeof(wri)); if (wri != numfaces) { printf("Fluidsim: error in reading data from file.\n"); if (mesh) BKE_id_free(NULL, mesh); gzclose(gzf); MEM_freeN(normals); return NULL; } /* read triangles from file */ mp = mesh->mpoly; ml = mesh->mloop; for (i = 0; i < numfaces; i++, mp++, ml += 3) { int face[3]; gotBytes = gzread(gzf, face, sizeof(int) * 3); /* initialize from existing face */ mp->mat_nr = mp_mat_nr; mp->flag = mp_flag; mp->loopstart = i * 3; mp->totloop = 3; ml[0].v = face[0]; ml[1].v = face[1]; ml[2].v = face[2]; } gzclose(gzf); BKE_mesh_calc_edges(mesh, false, false); BKE_mesh_apply_vert_normals(mesh, (short (*)[3])normals); MEM_freeN(normals); // CDDM_calc_normals(result); return mesh; } void fluid_get_bb( MVert *mvert, int totvert, float obmat[4][4], /*RET*/ float start[3], /*RET*/ float size[3]) { float bbsx = 0.0, bbsy = 0.0, bbsz = 0.0; float bbex = 1.0, bbey = 1.0, bbez = 1.0; int i; float vec[3]; if (totvert == 0) { zero_v3(start); zero_v3(size); return; } copy_v3_v3(vec, mvert[0].co); mul_m4_v3(obmat, vec); bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2]; bbex = vec[0]; bbey = vec[1]; bbez = vec[2]; for (i = 1; i < totvert; i++) { copy_v3_v3(vec, mvert[i].co); mul_m4_v3(obmat, vec); if (vec[0] < bbsx) { bbsx = vec[0]; } if (vec[1] < bbsy) { bbsy = vec[1]; } if (vec[2] < bbsz) { bbsz = vec[2]; } if (vec[0] > bbex) { bbex = vec[0]; } if (vec[1] > bbey) { bbey = vec[1]; } if (vec[2] > bbez) { bbez = vec[2]; } } /* return values... */ if (start) { start[0] = bbsx; start[1] = bbsy; start[2] = bbsz; } if (size) { size[0] = bbex - bbsx; size[1] = bbey - bbsy; size[2] = bbez - bbsz; } } //------------------------------------------------------------------------------- // old interface //------------------------------------------------------------------------------- void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value) { Mesh *mesh; value[0] = '\0'; if (ob->type == OB_MESH) { /* use mesh bounding box and object scaling */ mesh = ob->data; fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0], fss->bbSize[1], fss->bbSize[2], fss->maxRefine, value); } } /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */ static void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, Mesh *mesh, char *filename) { int wri, i, j; float wrf; gzFile gzf; FluidsimSettings *fss = fluidmd->fss; int len = strlen(filename); int totvert = mesh->totvert; FluidVertexVelocity *velarray = NULL; /* mesh and vverts have to be valid from loading... */ if (fss->meshVelocities) MEM_freeN(fss->meshVelocities); if (len < 7) { return; } if (fss->domainNovecgen > 0) return; fss->meshVelocities = MEM_calloc_arrayN(mesh->totvert, sizeof(FluidVertexVelocity), "Fluidsim_velocities"); fss->totvert = totvert; velarray = fss->meshVelocities; /* .bobj.gz, correct filename * 87654321 */ filename[len - 6] = 'v'; filename[len - 5] = 'e'; filename[len - 4] = 'l'; gzf = BLI_gzopen(filename, "rb"); if (!gzf) { MEM_freeN(fss->meshVelocities); fss->meshVelocities = NULL; return; } gzread(gzf, &wri, sizeof(wri)); if (wri != totvert) { MEM_freeN(fss->meshVelocities); fss->meshVelocities = NULL; return; } for (i = 0; i < totvert; i++) { for (j = 0; j < 3; j++) { gzread(gzf, &wrf, sizeof(wrf)); velarray[i].vel[j] = wrf; } } gzclose(gzf); } static Mesh *fluidsim_read_cache( Object *ob, Mesh *orgmesh, FluidsimModifierData *fluidmd, int framenr, int useRenderParams) { int curFrame = framenr /* - 1 */ /*scene->r.sfra*/; /* start with 0 at start frame */ /* why start with 0 as start frame?? Animations + time are frozen for frame 0 anyway. (See physics_fluid.c for that. - DG */ /* If we start with frame 0, we need to remap all animation channels, too, because they will all be 1 frame late if using frame-1! - DG */ char targetFile[FILE_MAX]; FluidsimSettings *fss = fluidmd->fss; Mesh *newmesh = NULL; MPoly *mpoly; MPoly mp_example = {0}; const int displaymode = useRenderParams ? fss->renderDisplayMode : fss->guiDisplayMode; switch (displaymode) { case OB_FSDOM_GEOM: /* just display original object */ return NULL; case OB_FSDOM_PREVIEW: /* use preview mesh */ BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_PREVIEW_OBJ_FNAME); break; case OB_FSDOM_FINAL: /* use final mesh */ BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_FINAL_OBJ_FNAME); break; default: BLI_assert(!"Wrong fluidsim display type"); return NULL; } /* offset baked frame */ curFrame += fss->frameOffset; BLI_path_abs(targetFile, modifier_path_relbase_from_global(ob)); BLI_path_frame(targetFile, curFrame, 0); // fixed #frame-no /* assign material + flags to new mesh. * if there's no faces in original mesh, keep materials and flags unchanged */ mpoly = orgmesh->mpoly; if (mpoly) { mp_example = *mpoly; } /* else leave NULL'd */ newmesh = fluidsim_read_obj(targetFile, &mp_example); if (!newmesh) { /* switch, abort background rendering when fluidsim mesh is missing */ const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp if (G.background == 1) { if (BLI_getenv(strEnvName2)) { int elevel = atoi(BLI_getenv(strEnvName2)); if (elevel > 0) { printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n", strEnvName2, targetFile); exit(1); } } } /* display org. object upon failure which is in new mesh */ return NULL; } /* load vertex velocities, if they exist... * TODO? use generate flag as loading flag as well? * warning, needs original .bobj.gz mesh loading filename */ if (displaymode == OB_FSDOM_FINAL) { fluidsim_read_vel_cache(fluidmd, newmesh, targetFile); } else { if (fss->meshVelocities) MEM_freeN(fss->meshVelocities); fss->meshVelocities = NULL; } return newmesh; } #endif // WITH_MOD_FLUID Mesh *fluidsimModifier_do( FluidsimModifierData *fluidmd, const ModifierEvalContext *ctx, Mesh *mesh) { #ifdef WITH_MOD_FLUID Object *ob = ctx->object; Depsgraph *depsgraph = ctx->depsgraph; const bool useRenderParams = (ctx->flag & MOD_APPLY_RENDER) != 0; // const bool isFinalCalc = (ctx->flag & MOD_APPLY_USECACHE) != 0; Mesh *result = NULL; int framenr; FluidsimSettings *fss = NULL; framenr = (int)DEG_get_ctime(depsgraph); /* only handle fluidsim domains */ if (fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN)) return mesh; /* sanity check */ if (!fluidmd || !fluidmd->fss) return mesh; fss = fluidmd->fss; /* timescale not supported yet * clmd->sim_parms->timescale = timescale; */ /* support reversing of baked fluid frames here */ if ((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0)) { framenr = fss->lastgoodframe - framenr + 1; CLAMP(framenr, 1, fss->lastgoodframe); } /* try to read from cache */ /* if the frame is there, fine, otherwise don't do anything */ if ((result = fluidsim_read_cache(ob, mesh, fluidmd, framenr, useRenderParams))) return result; return mesh; #else /* unused */ UNUSED_VARS(fluidmd, ctx, mesh); return NULL; #endif }