This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/modifiers/intern/MOD_fluidsim_util.c
Bastien Montagne 54a5059fe9 Cleanup: finish modifiers' hearder re-order from rBe31f0fad9744c813.
For some reason forgot to handle half of modifier files...
2019-02-25 11:57:14 +01:00

552 lines
13 KiB
C

/*
* 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.
*/
/** \file
* \ingroup modifiers
*/
#include <stddef.h>
#include <zlib.h>
#include "BLI_utildefines.h"
#ifdef WITH_MOD_FLUID
# include "BLI_blenlib.h"
# include "BLI_math.h"
#endif
#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 "BKE_fluidsim.h" /* ensure definitions here match */
#include "BKE_mesh.h"
#ifdef WITH_MOD_FLUID
# include "BKE_global.h"
# include "BKE_library.h"
#endif
#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
}