/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2016 Blender Foundation. All rights reserved. */ /** \file * \ingroup intern_mantaflow */ #include #include #include #include #include #include "MANTA_main.h" #include "Python.h" #include "fluid_script.h" #include "liquid_script.h" #include "manta.h" #include "smoke_script.h" #include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_utildefines.h" #include "DNA_fluid_types.h" #include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" using std::cerr; using std::cout; using std::endl; using std::ifstream; using std::istringstream; using std::ofstream; using std::ostringstream; using std::to_string; atomic MANTA::solverID(0); int MANTA::with_debug(0); MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID), mMaxRes(fmd->domain->maxres) { if (with_debug) cout << "FLUID: " << mCurrentID << " with res(" << res[0] << ", " << res[1] << ", " << res[2] << ")" << endl; FluidDomainSettings *fds = fmd->domain; fds->fluid = this; mUsingLiquid = (fds->type == FLUID_DOMAIN_TYPE_LIQUID); mUsingSmoke = (fds->type == FLUID_DOMAIN_TYPE_GAS); mUsingNoise = (fds->flags & FLUID_DOMAIN_USE_NOISE) && mUsingSmoke; mUsingFractions = (fds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid; mUsingMesh = (fds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid; mUsingDiffusion = (fds->flags & FLUID_DOMAIN_USE_DIFFUSION) && mUsingLiquid; mUsingViscosity = (fds->flags & FLUID_DOMAIN_USE_VISCOSITY) && mUsingLiquid; mUsingMVel = (fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid; mUsingGuiding = (fds->flags & FLUID_DOMAIN_USE_GUIDE); mUsingDrops = (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid; mUsingBubbles = (fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && mUsingLiquid; mUsingFloats = (fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && mUsingLiquid; mUsingTracers = (fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) && mUsingLiquid; mUsingHeat = (fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) && mUsingSmoke; mUsingFire = (fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) && mUsingSmoke; mUsingColors = (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) && mUsingSmoke; mUsingObstacle = (fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE); mUsingInvel = (fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL); mUsingOutflow = (fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW); /* Simulation constants */ mResX = res[0]; /* Current size of domain (will adjust with adaptive domain). */ mResY = res[1]; mResZ = res[2]; mTotalCells = mResX * mResY * mResZ; mResGuiding = fds->res; /* Smoke low res grids. */ mDensity = nullptr; mShadow = nullptr; mHeat = nullptr; mVelocityX = nullptr; mVelocityY = nullptr; mVelocityZ = nullptr; mForceX = nullptr; mForceY = nullptr; mForceZ = nullptr; mFlame = nullptr; mFuel = nullptr; mReact = nullptr; mColorR = nullptr; mColorG = nullptr; mColorB = nullptr; mFlags = nullptr; mDensityIn = nullptr; mHeatIn = nullptr; mColorRIn = nullptr; mColorGIn = nullptr; mColorBIn = nullptr; mFuelIn = nullptr; mReactIn = nullptr; mEmissionIn = nullptr; mPressure = nullptr; /* Smoke high res grids. */ mDensityHigh = nullptr; mFlameHigh = nullptr; mFuelHigh = nullptr; mReactHigh = nullptr; mColorRHigh = nullptr; mColorGHigh = nullptr; mColorBHigh = nullptr; mTextureU = nullptr; mTextureV = nullptr; mTextureW = nullptr; mTextureU2 = nullptr; mTextureV2 = nullptr; mTextureW2 = nullptr; /* Fluid low res grids. */ mPhiIn = nullptr; mPhiStaticIn = nullptr; mPhiOutIn = nullptr; mPhiOutStaticIn = nullptr; mPhi = nullptr; /* Mesh. */ mMeshNodes = nullptr; mMeshTriangles = nullptr; mMeshVelocities = nullptr; /* Fluid obstacle. */ mPhiObsIn = nullptr; mPhiObsStaticIn = nullptr; mNumObstacle = nullptr; mObVelocityX = nullptr; mObVelocityY = nullptr; mObVelocityZ = nullptr; /* Fluid guiding. */ mPhiGuideIn = nullptr; mNumGuide = nullptr; mGuideVelocityX = nullptr; mGuideVelocityY = nullptr; mGuideVelocityZ = nullptr; /* Fluid initial velocity. */ mInVelocityX = nullptr; mInVelocityY = nullptr; mInVelocityZ = nullptr; /* Secondary particles. */ mFlipParticleData = nullptr; mFlipParticleVelocity = nullptr; mParticleData = nullptr; mParticleVelocity = nullptr; mParticleLife = nullptr; /* Cache read success indicators. */ mFlipFromFile = false; mMeshFromFile = false; mParticlesFromFile = false; /* Setup Mantaflow in Python. */ initializeMantaflow(); /* Initializa RNA map with values that Python will need. */ initializeRNAMap(fmd); bool initSuccess = true; /* Initialize Mantaflow variables in Python. */ /* Liquid. */ if (mUsingLiquid) { initSuccess &= initDomain(); initSuccess &= initLiquid(); if (mUsingObstacle) initSuccess &= initObstacle(); if (mUsingInvel) initSuccess &= initInVelocity(); if (mUsingOutflow) initSuccess &= initOutflow(); if (mUsingDrops || mUsingBubbles || mUsingFloats || mUsingTracers) { mUpresParticle = fds->particle_scale; mResXParticle = mUpresParticle * mResX; mResYParticle = mUpresParticle * mResY; mResZParticle = mUpresParticle * mResZ; mTotalCellsParticles = mResXParticle * mResYParticle * mResZParticle; initSuccess &= initSndParts(); initSuccess &= initLiquidSndParts(); } if (mUsingMesh) { mUpresMesh = fds->mesh_scale; mResXMesh = mUpresMesh * mResX; mResYMesh = mUpresMesh * mResY; mResZMesh = mUpresMesh * mResZ; mTotalCellsMesh = mResXMesh * mResYMesh * mResZMesh; /* Initialize Mantaflow variables in Python. */ initSuccess &= initMesh(); initSuccess &= initLiquidMesh(); } if (mUsingViscosity) { initSuccess &= initLiquidViscosity(); } if (mUsingDiffusion) { initSuccess &= initCurvature(); } if (mUsingGuiding) { mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res; initSuccess &= initGuiding(); } if (mUsingFractions) { initSuccess &= initFractions(); } } /* Smoke. */ if (mUsingSmoke) { initSuccess &= initDomain(); initSuccess &= initSmoke(); if (mUsingHeat) initSuccess &= initHeat(); if (mUsingFire) initSuccess &= initFire(); if (mUsingColors) initSuccess &= initColors(); if (mUsingObstacle) initSuccess &= initObstacle(); if (mUsingInvel) initSuccess &= initInVelocity(); if (mUsingOutflow) initSuccess &= initOutflow(); if (mUsingGuiding) { mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res; initSuccess &= initGuiding(); } if (mUsingNoise) { int amplify = fds->noise_scale; mResXNoise = amplify * mResX; mResYNoise = amplify * mResY; mResZNoise = amplify * mResZ; mTotalCellsHigh = mResXNoise * mResYNoise * mResZNoise; /* Initialize Mantaflow variables in Python. */ initSuccess &= initNoise(); initSuccess &= initSmokeNoise(); if (mUsingFire) initSuccess &= initFireHigh(); if (mUsingColors) initSuccess &= initColorsHigh(); } } /* All requested initializations must not fail in constructor. */ BLI_assert(initSuccess); (void)initSuccess; /* Ignored in release. */ updatePointers(fmd); } bool MANTA::initDomain(FluidModifierData *fmd) { /* Vector will hold all python commands that are to be executed. */ vector pythonCommands; /* Set manta debug level first. */ pythonCommands.push_back(manta_import + manta_debuglevel); ostringstream ss; ss << "set_manta_debuglevel(" << with_debug << ")"; pythonCommands.push_back(ss.str()); /* Now init basic fluid domain. */ string tmpString = fluid_variables + fluid_solver + fluid_alloc + fluid_cache_helper + fluid_bake_multiprocessing + fluid_bake_data + fluid_bake_noise + fluid_bake_mesh + fluid_bake_particles + fluid_bake_guiding + fluid_file_import + fluid_file_export + fluid_pre_step + fluid_post_step + fluid_adapt_time_step + fluid_time_stepping; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } bool MANTA::initNoise(FluidModifierData *fmd) { vector pythonCommands; string tmpString = fluid_variables_noise + fluid_solver_noise; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } bool MANTA::initSmoke(FluidModifierData *fmd) { vector pythonCommands; string tmpString = smoke_variables + smoke_alloc + smoke_adaptive_step + smoke_save_data + smoke_load_data + smoke_step; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } bool MANTA::initSmokeNoise(FluidModifierData *fmd) { vector pythonCommands; string tmpString = smoke_variables_noise + smoke_alloc_noise + smoke_wavelet_noise + smoke_save_noise + smoke_load_noise + smoke_step_noise; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingNoise = true; return runPythonString(pythonCommands); } bool MANTA::initHeat(FluidModifierData *fmd) { if (!mHeat) { vector pythonCommands; string tmpString = smoke_alloc_heat + smoke_with_heat; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingHeat = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initFire(FluidModifierData *fmd) { if (!mFuel) { vector pythonCommands; string tmpString = smoke_alloc_fire + smoke_with_fire; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingFire = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initFireHigh(FluidModifierData *fmd) { if (!mFuelHigh) { vector pythonCommands; string tmpString = smoke_alloc_fire_noise + smoke_with_fire; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingFire = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initColors(FluidModifierData *fmd) { if (!mColorR) { vector pythonCommands; string tmpString = smoke_alloc_colors + smoke_init_colors + smoke_with_colors; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingColors = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initColorsHigh(FluidModifierData *fmd) { if (!mColorRHigh) { vector pythonCommands; string tmpString = smoke_alloc_colors_noise + smoke_init_colors_noise + smoke_with_colors; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingColors = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initLiquid(FluidModifierData *fmd) { if (!mPhiIn) { vector pythonCommands; string tmpString = liquid_variables + liquid_alloc + liquid_init_phi + liquid_save_data + liquid_load_data + liquid_adaptive_step + liquid_step; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingLiquid = true; return runPythonString(pythonCommands); } return false; } bool MANTA::initMesh(FluidModifierData *fmd) { vector pythonCommands; string tmpString = fluid_variables_mesh + fluid_solver_mesh + liquid_load_mesh; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingMesh = true; return runPythonString(pythonCommands); } bool MANTA::initLiquidMesh(FluidModifierData *fmd) { vector pythonCommands; string tmpString = liquid_alloc_mesh + liquid_step_mesh + liquid_save_mesh; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingMesh = true; return runPythonString(pythonCommands); } bool MANTA::initLiquidViscosity(FluidModifierData *fmd) { vector pythonCommands; string tmpString = fluid_variables_viscosity + fluid_solver_viscosity + liquid_alloc_viscosity; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); mUsingViscosity = true; return runPythonString(pythonCommands); } bool MANTA::initCurvature(FluidModifierData *fmd) { std::vector pythonCommands; std::string finalString = parseScript(liquid_alloc_curvature, fmd); pythonCommands.push_back(finalString); mUsingDiffusion = true; return runPythonString(pythonCommands); } bool MANTA::initObstacle(FluidModifierData *fmd) { if (!mPhiObsIn) { vector pythonCommands; string tmpString = fluid_alloc_obstacle + fluid_with_obstacle; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return (mUsingObstacle = runPythonString(pythonCommands)); } return false; } bool MANTA::initGuiding(FluidModifierData *fmd) { if (!mPhiGuideIn) { vector pythonCommands; string tmpString = fluid_variables_guiding + fluid_solver_guiding + fluid_alloc_guiding + fluid_save_guiding + fluid_load_vel + fluid_load_guiding; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return (mUsingGuiding = runPythonString(pythonCommands)); } return false; } bool MANTA::initFractions(FluidModifierData *fmd) { vector pythonCommands; string tmpString = fluid_alloc_fractions + fluid_with_fractions; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return (mUsingFractions = runPythonString(pythonCommands)); } bool MANTA::initInVelocity(FluidModifierData *fmd) { if (!mInVelocityX) { vector pythonCommands; string tmpString = fluid_alloc_invel + fluid_with_invel; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return (mUsingInvel = runPythonString(pythonCommands)); } return false; } bool MANTA::initOutflow(FluidModifierData *fmd) { if (!mPhiOutIn) { vector pythonCommands; string tmpString = fluid_alloc_outflow + fluid_with_outflow; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return (mUsingOutflow = runPythonString(pythonCommands)); } return false; } bool MANTA::initSndParts(FluidModifierData *fmd) { vector pythonCommands; string tmpString = fluid_variables_particles + fluid_solver_particles; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } bool MANTA::initLiquidSndParts(FluidModifierData *fmd) { if (!mParticleData) { vector pythonCommands; string tmpString = liquid_alloc_particles + liquid_variables_particles + liquid_step_particles + fluid_with_sndparts + liquid_load_particles + liquid_save_particles; string finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } return false; } MANTA::~MANTA() { if (with_debug) cout << "~FLUID: " << mCurrentID << " with res(" << mResX << ", " << mResY << ", " << mResZ << ")" << endl; /* Destruction string for Python. */ string tmpString = ""; vector pythonCommands; bool result = false; tmpString += manta_import; tmpString += fluid_delete_all; /* Initializa RNA map with values that Python will need. */ initializeRNAMap(); /* Leave out fmd argument in parseScript since only looking up IDs. */ string finalString = parseScript(tmpString); pythonCommands.push_back(finalString); result = runPythonString(pythonCommands); /* WARNING: this causes crash on exit in the `cycles_volume_cpu/smoke_color` test, * freeing a single modifier ends up clearing the shared module. * For this to be handled properly there would need to be a initialize/free * function for global data. */ #if 0 MANTA::terminateMantaflow(); #endif BLI_assert(result); UNUSED_VARS(result); } /** * Copied from `PyC_DefaultNameSpace` in Blender. * with some differences: * - Doesn't touch `sys.modules`, use #manta_python_main_module_activate instead. * - Returns the module instead of the modules `dict`. * */ static PyObject *manta_python_main_module_create(const char *filename) { PyObject *builtins = PyEval_GetBuiltins(); PyObject *mod_main = PyModule_New("__main__"); PyModule_AddStringConstant(mod_main, "__name__", "__main__"); if (filename) { /* __file__ mainly for nice UI'ness * NOTE: this won't map to a real file when executing text-blocks and buttons. */ PyModule_AddObject(mod_main, "__file__", PyUnicode_InternFromString(filename)); } PyModule_AddObject(mod_main, "__builtins__", builtins); Py_INCREF(builtins); /* AddObject steals a reference */ return mod_main; } static void manta_python_main_module_activate(PyObject *mod_main) { PyObject *modules = PyImport_GetModuleDict(); PyObject *main_mod_cmp = PyDict_GetItemString(modules, "__main__"); if (mod_main == main_mod_cmp) { return; } /* NOTE: we could remove the reference to `mod_main` here, but as it's know to be removed * accept that there is temporarily an extra reference. */ PyDict_SetItemString(modules, "__main__", mod_main); } static void manta_python_main_module_backup(PyObject **r_main_mod) { PyObject *modules = PyImport_GetModuleDict(); *r_main_mod = PyDict_GetItemString(modules, "__main__"); Py_XINCREF(*r_main_mod); /* don't free */ } static void manta_python_main_module_restore(PyObject *main_mod) { PyObject *modules = PyImport_GetModuleDict(); PyDict_SetItemString(modules, "__main__", main_mod); Py_XDECREF(main_mod); } /** * Mantaflow stores many variables in the globals() dict of the __main__ module. To be able to * access these variables, the same __main__ module has to be used every time. * * Unfortunately, we also depend on the fact that mantaflow dumps variables into this module using * #PyRun_String. So we can't easily create a separate module without changing mantaflow. */ static PyObject *manta_main_module = nullptr; static void manta_python_main_module_clear() { if (manta_main_module) { Py_DECREF(manta_main_module); manta_main_module = nullptr; } } static PyObject *manta_python_main_module_ensure() { if (!manta_main_module) { manta_main_module = manta_python_main_module_create(""); } return manta_main_module; } bool MANTA::runPythonString(vector commands) { bool success = true; PyGILState_STATE gilstate = PyGILState_Ensure(); /* Temporarily set `sys.modules["__main__"]` as some Python modules expect this. */ PyObject *main_mod_backup; manta_python_main_module_backup(&main_mod_backup); /* If we never want to run this when the module isn't initialize, * assign with `manta_python_main_module_ensure()`. */ BLI_assert(manta_main_module != nullptr); manta_python_main_module_activate(manta_main_module); for (vector::iterator it = commands.begin(); it != commands.end(); ++it) { string command = *it; PyObject *globals_dict = PyModule_GetDict(manta_main_module); PyObject *return_value = PyRun_String( command.c_str(), Py_file_input, globals_dict, globals_dict); if (return_value == nullptr) { success = false; if (PyErr_Occurred()) { PyErr_Print(); } } else { Py_DECREF(return_value); } } manta_python_main_module_restore(main_mod_backup); PyGILState_Release(gilstate); BLI_assert(success); return success; } void MANTA::initializeMantaflow() { if (with_debug) cout << "Fluid: Initializing Mantaflow framework" << endl; string filename = "manta_scene_" + to_string(mCurrentID) + ".py"; vector fill = vector(); /* Initialize extension classes and wrappers. */ srand(0); PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *manta_main_module = manta_python_main_module_ensure(); PyObject *globals_dict = PyModule_GetDict(manta_main_module); Pb::setup(false, filename, fill, globals_dict); /* Namespace from Mantaflow (registry). */ PyGILState_Release(gilstate); } void MANTA::terminateMantaflow() { if (with_debug) cout << "Fluid: Releasing Mantaflow framework" << endl; PyGILState_STATE gilstate = PyGILState_Ensure(); Pb::finalize(false); /* Namespace from Mantaflow (registry). */ manta_python_main_module_clear(); PyGILState_Release(gilstate); } static string getCacheFileEnding(char cache_format) { if (MANTA::with_debug) cout << "MANTA::getCacheFileEnding()" << endl; switch (cache_format) { case FLUID_DOMAIN_FILE_UNI: return FLUID_DOMAIN_EXTENSION_UNI; case FLUID_DOMAIN_FILE_OPENVDB: return FLUID_DOMAIN_EXTENSION_OPENVDB; case FLUID_DOMAIN_FILE_RAW: return FLUID_DOMAIN_EXTENSION_RAW; case FLUID_DOMAIN_FILE_BIN_OBJECT: return FLUID_DOMAIN_EXTENSION_BINOBJ; case FLUID_DOMAIN_FILE_OBJECT: return FLUID_DOMAIN_EXTENSION_OBJ; default: cerr << "Fluid Error -- Could not find file extension. Using default file extension." << endl; return FLUID_DOMAIN_EXTENSION_UNI; } } static string getBooleanString(int value) { return (value) ? "True" : "False"; } void MANTA::initializeRNAMap(FluidModifierData *fmd) { if (with_debug) cout << "MANTA::initializeRNAMap()" << endl; mRNAMap["ID"] = to_string(mCurrentID); if (!fmd) { if (with_debug) cout << "Fluid: No modifier data given in RNA map setup - returning early" << endl; return; } FluidDomainSettings *fds = fmd->domain; bool is2D = (fds->solver_res == 2); string borderCollisions = ""; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0) borderCollisions += "x"; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0) borderCollisions += "X"; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0) borderCollisions += "y"; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0) borderCollisions += "Y"; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_BOTTOM) == 0) borderCollisions += "z"; if ((fds->border_collisions & FLUID_DOMAIN_BORDER_TOP) == 0) borderCollisions += "Z"; string particleTypesStr = ""; if (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) particleTypesStr += "PtypeSpray"; if (fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) { if (!particleTypesStr.empty()) particleTypesStr += "|"; particleTypesStr += "PtypeBubble"; } if (fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) { if (!particleTypesStr.empty()) particleTypesStr += "|"; particleTypesStr += "PtypeFoam"; } if (fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) { if (!particleTypesStr.empty()) particleTypesStr += "|"; particleTypesStr += "PtypeTracer"; } if (particleTypesStr.empty()) particleTypesStr = "0"; int particleTypes = (FLUID_DOMAIN_PARTICLE_SPRAY | FLUID_DOMAIN_PARTICLE_BUBBLE | FLUID_DOMAIN_PARTICLE_FOAM | FLUID_DOMAIN_PARTICLE_TRACER); string cacheDirectory(fds->cache_directory); float viscosity = fds->viscosity_base * pow(10.0f, -fds->viscosity_exponent); float domainSize = MAX3(fds->global_size[0], fds->global_size[1], fds->global_size[2]); string vdbCompressionMethod = "Compression_None"; if (fds->openvdb_compression == VDB_COMPRESSION_NONE) vdbCompressionMethod = "Compression_None"; else if (fds->openvdb_compression == VDB_COMPRESSION_ZIP) vdbCompressionMethod = "Compression_Zip"; else if (fds->openvdb_compression == VDB_COMPRESSION_BLOSC) vdbCompressionMethod = "Compression_Blosc"; string vdbPrecisionHalf = "Precision_Half"; if (fds->openvdb_data_depth == VDB_PRECISION_FULL_FLOAT) vdbPrecisionHalf = "Precision_Full"; else if (fds->openvdb_data_depth == VDB_PRECISION_HALF_FLOAT) vdbPrecisionHalf = "Precision_Half"; else if (fds->openvdb_data_depth == VDB_PRECISION_MINI_FLOAT) vdbPrecisionHalf = "Precision_Mini"; mRNAMap["USING_SMOKE"] = getBooleanString(fds->type == FLUID_DOMAIN_TYPE_GAS); mRNAMap["USING_LIQUID"] = getBooleanString(fds->type == FLUID_DOMAIN_TYPE_LIQUID); mRNAMap["USING_COLORS"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS); mRNAMap["USING_HEAT"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT); mRNAMap["USING_FIRE"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE); mRNAMap["USING_NOISE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_NOISE); mRNAMap["USING_OBSTACLE"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE); mRNAMap["USING_GUIDING"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_GUIDE); mRNAMap["USING_INVEL"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL); mRNAMap["USING_OUTFLOW"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW); mRNAMap["USING_LOG_DISSOLVE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DISSOLVE_LOG); mRNAMap["USING_DISSOLVE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DISSOLVE); mRNAMap["DOMAIN_CLOSED"] = getBooleanString(borderCollisions.compare("") == 0); mRNAMap["CACHE_RESUMABLE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE); mRNAMap["USING_ADAPTIVETIME"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_TIME); mRNAMap["USING_SPEEDVECTORS"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS); mRNAMap["USING_FRACTIONS"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_FRACTIONS); mRNAMap["DELETE_IN_OBSTACLE"] = getBooleanString(fds->flags & FLUID_DOMAIN_DELETE_IN_OBSTACLE); mRNAMap["USING_DIFFUSION"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DIFFUSION); mRNAMap["USING_MESH"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_MESH); mRNAMap["USING_IMPROVED_MESH"] = getBooleanString(fds->mesh_generator == FLUID_DOMAIN_MESH_IMPROVED); mRNAMap["USING_SNDPARTS"] = getBooleanString(fds->particle_type & particleTypes); mRNAMap["SNDPARTICLE_BOUNDARY_DELETE"] = getBooleanString(fds->sndparticle_boundary == SNDPARTICLE_BOUNDARY_DELETE); mRNAMap["SNDPARTICLE_BOUNDARY_PUSHOUT"] = getBooleanString(fds->sndparticle_boundary == SNDPARTICLE_BOUNDARY_PUSHOUT); mRNAMap["SOLVER_DIM"] = to_string(fds->solver_res); mRNAMap["BOUND_CONDITIONS"] = borderCollisions; mRNAMap["BOUNDARY_WIDTH"] = to_string(fds->boundary_width); mRNAMap["RES"] = to_string(mMaxRes); mRNAMap["RESX"] = to_string(mResX); mRNAMap["RESY"] = (is2D) ? to_string(mResZ) : to_string(mResY); mRNAMap["RESZ"] = (is2D) ? to_string(1) : to_string(mResZ); mRNAMap["TIME_SCALE"] = to_string(fds->time_scale); mRNAMap["FRAME_LENGTH"] = to_string(fds->frame_length); mRNAMap["CFL"] = to_string(fds->cfl_condition); mRNAMap["DT"] = to_string(fds->dt); mRNAMap["TIMESTEPS_MIN"] = to_string(fds->timesteps_minimum); mRNAMap["TIMESTEPS_MAX"] = to_string(fds->timesteps_maximum); mRNAMap["TIME_TOTAL"] = to_string(fds->time_total); mRNAMap["TIME_PER_FRAME"] = to_string(fds->time_per_frame); mRNAMap["VORTICITY"] = to_string(fds->vorticity); mRNAMap["FLAME_VORTICITY"] = to_string(fds->flame_vorticity); mRNAMap["NOISE_SCALE"] = to_string(fds->noise_scale); mRNAMap["MESH_SCALE"] = to_string(fds->mesh_scale); mRNAMap["PARTICLE_SCALE"] = to_string(fds->particle_scale); mRNAMap["NOISE_RESX"] = to_string(mResXNoise); mRNAMap["NOISE_RESY"] = (is2D) ? to_string(mResZNoise) : to_string(mResYNoise); mRNAMap["NOISE_RESZ"] = (is2D) ? to_string(1) : to_string(mResZNoise); mRNAMap["MESH_RESX"] = to_string(mResXMesh); mRNAMap["MESH_RESY"] = (is2D) ? to_string(mResZMesh) : to_string(mResYMesh); mRNAMap["MESH_RESZ"] = (is2D) ? to_string(1) : to_string(mResZMesh); mRNAMap["PARTICLE_RESX"] = to_string(mResXParticle); mRNAMap["PARTICLE_RESY"] = (is2D) ? to_string(mResZParticle) : to_string(mResYParticle); mRNAMap["PARTICLE_RESZ"] = (is2D) ? to_string(1) : to_string(mResZParticle); mRNAMap["GUIDING_RESX"] = to_string(mResGuiding[0]); mRNAMap["GUIDING_RESY"] = (is2D) ? to_string(mResGuiding[2]) : to_string(mResGuiding[1]); mRNAMap["GUIDING_RESZ"] = (is2D) ? to_string(1) : to_string(mResGuiding[2]); mRNAMap["MIN_RESX"] = to_string(fds->res_min[0]); mRNAMap["MIN_RESY"] = to_string(fds->res_min[1]); mRNAMap["MIN_RESZ"] = to_string(fds->res_min[2]); mRNAMap["BASE_RESX"] = to_string(fds->base_res[0]); mRNAMap["BASE_RESY"] = to_string(fds->base_res[1]); mRNAMap["BASE_RESZ"] = to_string(fds->base_res[2]); mRNAMap["WLT_STR"] = to_string(fds->noise_strength); mRNAMap["NOISE_POSSCALE"] = to_string(fds->noise_pos_scale); mRNAMap["NOISE_TIMEANIM"] = to_string(fds->noise_time_anim); mRNAMap["COLOR_R"] = to_string(fds->active_color[0]); mRNAMap["COLOR_G"] = to_string(fds->active_color[1]); mRNAMap["COLOR_B"] = to_string(fds->active_color[2]); mRNAMap["BUOYANCY_ALPHA"] = to_string(fds->alpha); mRNAMap["BUOYANCY_BETA"] = to_string(fds->beta); mRNAMap["DISSOLVE_SPEED"] = to_string(fds->diss_speed); mRNAMap["BURNING_RATE"] = to_string(fds->burning_rate); mRNAMap["FLAME_SMOKE"] = to_string(fds->flame_smoke); mRNAMap["IGNITION_TEMP"] = to_string(fds->flame_ignition); mRNAMap["MAX_TEMP"] = to_string(fds->flame_max_temp); mRNAMap["FLAME_SMOKE_COLOR_X"] = to_string(fds->flame_smoke_color[0]); mRNAMap["FLAME_SMOKE_COLOR_Y"] = to_string(fds->flame_smoke_color[1]); mRNAMap["FLAME_SMOKE_COLOR_Z"] = to_string(fds->flame_smoke_color[2]); mRNAMap["CURRENT_FRAME"] = to_string(int(fmd->time)); mRNAMap["START_FRAME"] = to_string(fds->cache_frame_start); mRNAMap["END_FRAME"] = to_string(fds->cache_frame_end); mRNAMap["CACHE_DATA_FORMAT"] = getCacheFileEnding(fds->cache_data_format); mRNAMap["CACHE_MESH_FORMAT"] = getCacheFileEnding(fds->cache_mesh_format); mRNAMap["CACHE_NOISE_FORMAT"] = getCacheFileEnding(fds->cache_noise_format); mRNAMap["CACHE_PARTICLE_FORMAT"] = getCacheFileEnding(fds->cache_particle_format); mRNAMap["USING_APIC"] = getBooleanString(fds->simulation_method == FLUID_DOMAIN_METHOD_APIC); mRNAMap["FLIP_RATIO"] = to_string(fds->flip_ratio); mRNAMap["PARTICLE_RANDOMNESS"] = to_string(fds->particle_randomness); mRNAMap["PARTICLE_NUMBER"] = to_string(fds->particle_number); mRNAMap["PARTICLE_MINIMUM"] = to_string(fds->particle_minimum); mRNAMap["PARTICLE_MAXIMUM"] = to_string(fds->particle_maximum); mRNAMap["PARTICLE_RADIUS"] = to_string(fds->particle_radius); mRNAMap["FRACTIONS_THRESHOLD"] = to_string(fds->fractions_threshold); mRNAMap["FRACTIONS_DISTANCE"] = to_string(fds->fractions_distance); mRNAMap["MESH_CONCAVE_UPPER"] = to_string(fds->mesh_concave_upper); mRNAMap["MESH_CONCAVE_LOWER"] = to_string(fds->mesh_concave_lower); mRNAMap["MESH_PARTICLE_RADIUS"] = to_string(fds->mesh_particle_radius); mRNAMap["MESH_SMOOTHEN_POS"] = to_string(fds->mesh_smoothen_pos); mRNAMap["MESH_SMOOTHEN_NEG"] = to_string(fds->mesh_smoothen_neg); mRNAMap["PARTICLE_BAND_WIDTH"] = to_string(fds->particle_band_width); mRNAMap["SNDPARTICLE_TAU_MIN_WC"] = to_string(fds->sndparticle_tau_min_wc); mRNAMap["SNDPARTICLE_TAU_MAX_WC"] = to_string(fds->sndparticle_tau_max_wc); mRNAMap["SNDPARTICLE_TAU_MIN_TA"] = to_string(fds->sndparticle_tau_min_ta); mRNAMap["SNDPARTICLE_TAU_MAX_TA"] = to_string(fds->sndparticle_tau_max_ta); mRNAMap["SNDPARTICLE_TAU_MIN_K"] = to_string(fds->sndparticle_tau_min_k); mRNAMap["SNDPARTICLE_TAU_MAX_K"] = to_string(fds->sndparticle_tau_max_k); mRNAMap["SNDPARTICLE_K_WC"] = to_string(fds->sndparticle_k_wc); mRNAMap["SNDPARTICLE_K_TA"] = to_string(fds->sndparticle_k_ta); mRNAMap["SNDPARTICLE_K_B"] = to_string(fds->sndparticle_k_b); mRNAMap["SNDPARTICLE_K_D"] = to_string(fds->sndparticle_k_d); mRNAMap["SNDPARTICLE_L_MIN"] = to_string(fds->sndparticle_l_min); mRNAMap["SNDPARTICLE_L_MAX"] = to_string(fds->sndparticle_l_max); mRNAMap["SNDPARTICLE_POTENTIAL_RADIUS"] = to_string(fds->sndparticle_potential_radius); mRNAMap["SNDPARTICLE_UPDATE_RADIUS"] = to_string(fds->sndparticle_update_radius); mRNAMap["LIQUID_SURFACE_TENSION"] = to_string(fds->surface_tension); mRNAMap["FLUID_VISCOSITY"] = to_string(viscosity); mRNAMap["FLUID_DOMAIN_SIZE"] = to_string(domainSize); mRNAMap["FLUID_DOMAIN_SIZE_X"] = to_string(fds->global_size[0]); mRNAMap["FLUID_DOMAIN_SIZE_Y"] = to_string(fds->global_size[1]); mRNAMap["FLUID_DOMAIN_SIZE_Z"] = to_string(fds->global_size[2]); mRNAMap["SNDPARTICLE_TYPES"] = particleTypesStr; mRNAMap["GUIDING_ALPHA"] = to_string(fds->guide_alpha); mRNAMap["GUIDING_BETA"] = to_string(fds->guide_beta); mRNAMap["GUIDING_FACTOR"] = to_string(fds->guide_vel_factor); mRNAMap["GRAVITY_X"] = to_string(fds->gravity_final[0]); mRNAMap["GRAVITY_Y"] = to_string(fds->gravity_final[1]); mRNAMap["GRAVITY_Z"] = to_string(fds->gravity_final[2]); mRNAMap["CACHE_DIR"] = cacheDirectory; mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod; mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf; mRNAMap["CLIP_OPENVDB"] = to_string(fds->clipping); mRNAMap["PP_PARTICLE_MAXIMUM"] = to_string(fds->sys_particle_maximum); mRNAMap["USING_VISCOSITY"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_VISCOSITY); mRNAMap["VISCOSITY_VALUE"] = to_string(fds->viscosity_value); /* Fluid object names. */ mRNAMap["NAME_FLAGS"] = FLUID_NAME_FLAGS; mRNAMap["NAME_VELOCITY"] = FLUID_NAME_VELOCITY; mRNAMap["NAME_VELOCITYTMP"] = FLUID_NAME_VELOCITYTMP; mRNAMap["NAME_VELOCITY_X"] = FLUID_NAME_VELOCITYX; mRNAMap["NAME_VELOCITY_Y"] = FLUID_NAME_VELOCITYY; mRNAMap["NAME_VELOCITY_Z"] = FLUID_NAME_VELOCITYZ; mRNAMap["NAME_PRESSURE"] = FLUID_NAME_PRESSURE; mRNAMap["NAME_PHIOBS"] = FLUID_NAME_PHIOBS; mRNAMap["NAME_PHISIN"] = FLUID_NAME_PHISIN; mRNAMap["NAME_PHIIN"] = FLUID_NAME_PHIIN; mRNAMap["NAME_PHIOUT"] = FLUID_NAME_PHIOUT; mRNAMap["NAME_FORCES"] = FLUID_NAME_FORCES; mRNAMap["NAME_FORCES_X"] = FLUID_NAME_FORCE_X; mRNAMap["NAME_FORCES_Y"] = FLUID_NAME_FORCE_Y; mRNAMap["NAME_FORCES_Z"] = FLUID_NAME_FORCE_Z; mRNAMap["NAME_NUMOBS"] = FLUID_NAME_NUMOBS; mRNAMap["NAME_PHIOBSSIN"] = FLUID_NAME_PHIOBSSIN; mRNAMap["NAME_PHIOBSIN"] = FLUID_NAME_PHIOBSIN; mRNAMap["NAME_OBVEL"] = FLUID_NAME_OBVEL; mRNAMap["NAME_OBVELC"] = FLUID_NAME_OBVELC; mRNAMap["NAME_OBVEL_X"] = FLUID_NAME_OBVEL_X; mRNAMap["NAME_OBVEL_Y"] = FLUID_NAME_OBVEL_Y; mRNAMap["NAME_OBVEL_Z"] = FLUID_NAME_OBVEL_Z; mRNAMap["NAME_FRACTIONS"] = FLUID_NAME_FRACTIONS; mRNAMap["NAME_INVELC"] = FLUID_NAME_INVELC; mRNAMap["NAME_INVEL_X"] = FLUID_NAME_INVEL_X; mRNAMap["NAME_INVEL_Y"] = FLUID_NAME_INVEL_Y; mRNAMap["NAME_INVEL_Z"] = FLUID_NAME_INVEL_Z; mRNAMap["NAME_PHIOUTSIN"] = FLUID_NAME_PHIOUTSIN; mRNAMap["NAME_PHIOUTIN"] = FLUID_NAME_PHIOUTIN; /* Smoke object names. */ mRNAMap["NAME_SHADOW"] = FLUID_NAME_SHADOW; mRNAMap["NAME_EMISSION"] = FLUID_NAME_EMISSION; mRNAMap["NAME_EMISSIONIN"] = FLUID_NAME_EMISSIONIN; mRNAMap["NAME_DENSITY"] = FLUID_NAME_DENSITY; mRNAMap["NAME_DENSITYIN"] = FLUID_NAME_DENSITYIN; mRNAMap["NAME_HEAT"] = FLUID_NAME_HEAT; mRNAMap["NAME_HEATIN"] = FLUID_NAME_HEATIN; mRNAMap["NAME_TEMPERATURE"] = FLUID_NAME_TEMPERATURE; mRNAMap["NAME_TEMPERATUREIN"] = FLUID_NAME_TEMPERATUREIN; mRNAMap["NAME_COLORR"] = FLUID_NAME_COLORR; mRNAMap["NAME_COLORG"] = FLUID_NAME_COLORG; mRNAMap["NAME_COLORB"] = FLUID_NAME_COLORB; mRNAMap["NAME_COLORRIN"] = FLUID_NAME_COLORRIN; mRNAMap["NAME_COLORGIN"] = FLUID_NAME_COLORGIN; mRNAMap["NAME_COLORBIN"] = FLUID_NAME_COLORBIN; mRNAMap["NAME_FLAME"] = FLUID_NAME_FLAME; mRNAMap["NAME_FUEL"] = FLUID_NAME_FUEL; mRNAMap["NAME_REACT"] = FLUID_NAME_REACT; mRNAMap["NAME_FUELIN"] = FLUID_NAME_FUELIN; mRNAMap["NAME_REACTIN"] = FLUID_NAME_REACTIN; /* Liquid object names. */ mRNAMap["NAME_PHIPARTS"] = FLUID_NAME_PHIPARTS; mRNAMap["NAME_PHI"] = FLUID_NAME_PHI; mRNAMap["NAME_PHITMP"] = FLUID_NAME_PHITMP; mRNAMap["NAME_VELOLD"] = FLUID_NAME_VELOCITYOLD; mRNAMap["NAME_VELPARTS"] = FLUID_NAME_VELOCITYPARTS; mRNAMap["NAME_MAPWEIGHTS"] = FLUID_NAME_MAPWEIGHTS; mRNAMap["NAME_PP"] = FLUID_NAME_PP; mRNAMap["NAME_PVEL"] = FLUID_NAME_PVEL; mRNAMap["NAME_PARTS"] = FLUID_NAME_PARTS; mRNAMap["NAME_PARTSVELOCITY"] = FLUID_NAME_PARTSVELOCITY; mRNAMap["NAME_PINDEX"] = FLUID_NAME_PINDEX; mRNAMap["NAME_GPI"] = FLUID_NAME_GPI; mRNAMap["NAME_CURVATURE"] = FLUID_NAME_CURVATURE; /* Noise object names. */ mRNAMap["NAME_VELOCITY_NOISE"] = FLUID_NAME_VELOCITY_NOISE; mRNAMap["NAME_DENSITY_NOISE"] = FLUID_NAME_DENSITY_NOISE; mRNAMap["NAME_PHIIN_NOISE"] = FLUID_NAME_PHIIN_NOISE; mRNAMap["NAME_PHIOUT_NOISE"] = FLUID_NAME_PHIOUT_NOISE; mRNAMap["NAME_PHIOBS_NOISE"] = FLUID_NAME_PHIOBS_NOISE; mRNAMap["NAME_FLAGS_NOISE"] = FLUID_NAME_FLAGS_NOISE; mRNAMap["NAME_TMPIN_NOISE"] = FLUID_NAME_TMPIN_NOISE; mRNAMap["NAME_EMISSIONIN_NOISE"] = FLUID_NAME_EMISSIONIN_NOISE; mRNAMap["NAME_ENERGY"] = FLUID_NAME_ENERGY; mRNAMap["NAME_TMPFLAGS"] = FLUID_NAME_TMPFLAGS; mRNAMap["NAME_TEXTURE_U"] = FLUID_NAME_TEXTURE_U; mRNAMap["NAME_TEXTURE_V"] = FLUID_NAME_TEXTURE_V; mRNAMap["NAME_TEXTURE_W"] = FLUID_NAME_TEXTURE_W; mRNAMap["NAME_TEXTURE_U2"] = FLUID_NAME_TEXTURE_U2; mRNAMap["NAME_TEXTURE_V2"] = FLUID_NAME_TEXTURE_V2; mRNAMap["NAME_TEXTURE_W2"] = FLUID_NAME_TEXTURE_W2; mRNAMap["NAME_UV0"] = FLUID_NAME_UV0; mRNAMap["NAME_UV1"] = FLUID_NAME_UV1; mRNAMap["NAME_COLORR_NOISE"] = FLUID_NAME_COLORR_NOISE; mRNAMap["NAME_COLORG_NOISE"] = FLUID_NAME_COLORG_NOISE; mRNAMap["NAME_COLORB_NOISE"] = FLUID_NAME_COLORB_NOISE; mRNAMap["NAME_FLAME_NOISE"] = FLUID_NAME_FLAME_NOISE; mRNAMap["NAME_FUEL_NOISE"] = FLUID_NAME_FUEL_NOISE; mRNAMap["NAME_REACT_NOISE"] = FLUID_NAME_REACT_NOISE; /* Mesh object names. */ mRNAMap["NAME_PHIPARTS_MESH"] = FLUID_NAME_PHIPARTS_MESH; mRNAMap["NAME_PHI_MESH"] = FLUID_NAME_PHI_MESH; mRNAMap["NAME_PP_MESH"] = FLUID_NAME_PP_MESH; mRNAMap["NAME_FLAGS_MESH"] = FLUID_NAME_FLAGS_MESH; mRNAMap["NAME_LMESH"] = FLUID_NAME_LMESH; mRNAMap["NAME_VELOCITYVEC_MESH"] = FLUID_NAME_VELOCITYVEC_MESH; mRNAMap["NAME_VELOCITY_MESH"] = FLUID_NAME_VELOCITY_MESH; mRNAMap["NAME_PINDEX_MESH"] = FLUID_NAME_PINDEX_MESH; mRNAMap["NAME_GPI_MESH"] = FLUID_NAME_GPI_MESH; /* Particles object names. */ mRNAMap["NAME_PP_PARTICLES"] = FLUID_NAME_PP_PARTICLES; mRNAMap["NAME_PVEL_PARTICLES"] = FLUID_NAME_PVEL_PARTICLES; mRNAMap["NAME_PFORCE_PARTICLES"] = FLUID_NAME_PFORCE_PARTICLES; mRNAMap["NAME_PLIFE_PARTICLES"] = FLUID_NAME_PLIFE_PARTICLES; mRNAMap["NAME_PARTS_PARTICLES"] = FLUID_NAME_PARTS_PARTICLES; mRNAMap["NAME_PARTSVEL_PARTICLES"] = FLUID_NAME_PARTSVEL_PARTICLES; mRNAMap["NAME_PARTSFORCE_PARTICLES"] = FLUID_NAME_PARTSFORCE_PARTICLES; mRNAMap["NAME_PARTSLIFE_PARTICLES"] = FLUID_NAME_PARTSLIFE_PARTICLES; mRNAMap["NAME_VELOCITY_PARTICLES"] = FLUID_NAME_VELOCITY_PARTICLES; mRNAMap["NAME_FLAGS_PARTICLES"] = FLUID_NAME_FLAGS_PARTICLES; mRNAMap["NAME_PHI_PARTICLES"] = FLUID_NAME_PHI_PARTICLES; mRNAMap["NAME_PHIOBS_PARTICLES"] = FLUID_NAME_PHIOBS_PARTICLES; mRNAMap["NAME_PHIOUT_PARTICLES"] = FLUID_NAME_PHIOUT_PARTICLES; mRNAMap["NAME_NORMAL_PARTICLES"] = FLUID_NAME_NORMAL_PARTICLES; mRNAMap["NAME_NEIGHBORRATIO_PARTICLES"] = FLUID_NAME_NEIGHBORRATIO_PARTICLES; mRNAMap["NAME_TRAPPEDAIR_PARTICLES"] = FLUID_NAME_TRAPPEDAIR_PARTICLES; mRNAMap["NAME_WAVECREST_PARTICLES"] = FLUID_NAME_WAVECREST_PARTICLES; mRNAMap["NAME_KINETICENERGY_PARTICLES"] = FLUID_NAME_KINETICENERGY_PARTICLES; /* Guiding object names. */ mRNAMap["NAME_VELT"] = FLUID_NAME_VELT; mRNAMap["NAME_WEIGHTGUIDE"] = FLUID_NAME_WEIGHTGUIDE; mRNAMap["NAME_NUMGUIDES"] = FLUID_NAME_NUMGUIDES; mRNAMap["NAME_PHIGUIDEIN"] = FLUID_NAME_PHIGUIDEIN; mRNAMap["NAME_GUIDEVELC"] = FLUID_NAME_GUIDEVELC; mRNAMap["NAME_GUIDEVEL_X"] = FLUID_NAME_GUIDEVEL_X; mRNAMap["NAME_GUIDEVEL_Y"] = FLUID_NAME_GUIDEVEL_Y; mRNAMap["NAME_GUIDEVEL_Z"] = FLUID_NAME_GUIDEVEL_Z; mRNAMap["NAME_VELOCITY_GUIDE"] = FLUID_NAME_VELOCITY_GUIDE; /* Cache file names. */ mRNAMap["NAME_CONFIG"] = FLUID_NAME_CONFIG; mRNAMap["NAME_DATA"] = FLUID_NAME_DATA; mRNAMap["NAME_NOISE"] = FLUID_NAME_NOISE; mRNAMap["NAME_MESH"] = FLUID_NAME_MESH; mRNAMap["NAME_PARTICLES"] = FLUID_NAME_PARTICLES; mRNAMap["NAME_GUIDING"] = FLUID_NAME_GUIDING; } string MANTA::getRealValue(const string &varName) { unordered_map::iterator it; it = mRNAMap.find(varName); if (it == mRNAMap.end()) { cerr << "Fluid Error -- variable " << varName << " not found in RNA map " << it->second << endl; return ""; } return it->second; } string MANTA::parseLine(const string &line) { if (line.size() == 0) return ""; string res = ""; int currPos = 0, start_del = 0, end_del = -1; bool readingVar = false; const char delimiter = '$'; while (currPos < line.size()) { if (line[currPos] == delimiter && !readingVar) { readingVar = true; start_del = currPos + 1; res += line.substr(end_del + 1, currPos - end_del - 1); } else if (line[currPos] == delimiter && readingVar) { readingVar = false; end_del = currPos; res += getRealValue(line.substr(start_del, currPos - start_del)); } currPos++; } res += line.substr(end_del + 1, line.size() - end_del); return res; } string MANTA::parseScript(const string &setup_string, FluidModifierData *fmd) { if (MANTA::with_debug) cout << "MANTA::parseScript()" << endl; istringstream f(setup_string); ostringstream res; string line = ""; /* Update RNA map if modifier data is handed over. */ if (fmd) { initializeRNAMap(fmd); } while (getline(f, line)) { res << parseLine(line) << "\n"; } return res.str(); } /** Dirty hack: Needed to format paths from python code that is run via #PyRun_String. */ static string escapePath(string const &s) { string result = ""; for (char c : s) { if (c == '\\') { result += "\\\\"; } else if (c == '\'') { result += "\\\'"; } else { result += c; } } return result; } bool MANTA::writeConfiguration(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::writeConfiguration()" << endl; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_CONFIG); string format = FLUID_DOMAIN_EXTENSION_UNI; string file = getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr); /* Create 'config' subdir if it does not exist already. */ BLI_dir_create_recursive(directory.c_str()); /* Open new file with some compression. */ gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "wb1"); if (!gzf) { cerr << "Fluid Error -- Cannot open file " << file << endl; return false; } gzwrite(gzf, &fds->active_fields, sizeof(int)); gzwrite(gzf, &fds->res, 3 * sizeof(int)); gzwrite(gzf, &fds->dx, sizeof(float)); gzwrite(gzf, &fds->dt, sizeof(float)); gzwrite(gzf, &fds->p0, 3 * sizeof(float)); gzwrite(gzf, &fds->p1, 3 * sizeof(float)); gzwrite(gzf, &fds->dp0, 3 * sizeof(float)); gzwrite(gzf, &fds->shift, 3 * sizeof(int)); gzwrite(gzf, &fds->obj_shift_f, 3 * sizeof(float)); gzwrite(gzf, &fds->obmat, 16 * sizeof(float)); gzwrite(gzf, &fds->base_res, 3 * sizeof(int)); gzwrite(gzf, &fds->res_min, 3 * sizeof(int)); gzwrite(gzf, &fds->res_max, 3 * sizeof(int)); gzwrite(gzf, &fds->active_color, 3 * sizeof(float)); gzwrite(gzf, &fds->time_total, sizeof(int)); gzwrite(gzf, &FLUID_CACHE_VERSION, 4 * sizeof(char)); return (gzclose(gzf) == Z_OK); } bool MANTA::writeData(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::writeData()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_DATA); string volume_format = getCacheFileEnding(fds->cache_data_format); string resumable_cache = !(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True"; if (mUsingSmoke) { ss.str(""); ss << "smoke_save_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); } if (mUsingLiquid) { ss.str(""); ss << "liquid_save_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); } return runPythonString(pythonCommands); } bool MANTA::writeNoise(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::writeNoise()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_NOISE); string volume_format = getCacheFileEnding(fds->cache_data_format); string resumable_cache = !(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True"; if (mUsingSmoke && mUsingNoise) { ss.str(""); ss << "smoke_save_noise_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); } return runPythonString(pythonCommands); } bool MANTA::readConfiguration(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::readConfiguration()" << endl; FluidDomainSettings *fds = fmd->domain; float dummy; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_CONFIG); string format = FLUID_DOMAIN_EXTENSION_UNI; string file = getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr); if (!hasConfig(fmd, framenr)) return false; gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "rb"); /* Do some compression. */ if (!gzf) { cerr << "Fluid Error -- Cannot open file " << file << endl; return false; } gzread(gzf, &fds->active_fields, sizeof(int)); gzread(gzf, &fds->res, 3 * sizeof(int)); gzread(gzf, &fds->dx, sizeof(float)); gzread(gzf, &dummy, sizeof(float)); /* dt not needed right now. */ gzread(gzf, &fds->p0, 3 * sizeof(float)); gzread(gzf, &fds->p1, 3 * sizeof(float)); gzread(gzf, &fds->dp0, 3 * sizeof(float)); gzread(gzf, &fds->shift, 3 * sizeof(int)); gzread(gzf, &fds->obj_shift_f, 3 * sizeof(float)); gzread(gzf, &fds->obmat, 16 * sizeof(float)); gzread(gzf, &fds->base_res, 3 * sizeof(int)); gzread(gzf, &fds->res_min, 3 * sizeof(int)); gzread(gzf, &fds->res_max, 3 * sizeof(int)); gzread(gzf, &fds->active_color, 3 * sizeof(float)); gzread(gzf, &fds->time_total, sizeof(int)); gzread(gzf, &fds->cache_id, 4 * sizeof(char)); /* Older caches might have no id. */ fds->total_cells = fds->res[0] * fds->res[1] * fds->res[2]; return (gzclose(gzf) == Z_OK); } bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable) { if (with_debug) cout << "MANTA::readData()" << endl; if (!mUsingSmoke && !mUsingLiquid) return false; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; bool result = true; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_DATA); string volume_format = getCacheFileEnding(fds->cache_data_format); string resumable_cache = (!resumable) ? "False" : "True"; /* Sanity check: Are cache files present? */ if (!hasData(fmd, framenr)) return false; if (mUsingSmoke) { ss.str(""); ss << "smoke_load_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); result &= runPythonString(pythonCommands); return (mSmokeFromFile = result); } if (mUsingLiquid) { ss.str(""); ss << "liquid_load_data_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); result &= runPythonString(pythonCommands); return (mFlipFromFile = result); } return result; } bool MANTA::readNoise(FluidModifierData *fmd, int framenr, bool resumable) { if (with_debug) cout << "MANTA::readNoise()" << endl; if (!mUsingSmoke || !mUsingNoise) return false; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_NOISE); string resumable_cache = (!resumable) ? "False" : "True"; /* Support older caches which had more granular file format control. */ char format = (!strcmp(fds->cache_id, FLUID_CACHE_VERSION)) ? fds->cache_data_format : fds->cache_noise_format; string volume_format = getCacheFileEnding(format); /* Sanity check: Are cache files present? */ if (!hasNoise(fmd, framenr)) return false; ss.str(""); ss << "smoke_load_noise_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); return (mNoiseFromFile = runPythonString(pythonCommands)); } bool MANTA::readMesh(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::readMesh()" << endl; if (!mUsingLiquid || !mUsingMesh) return false; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_MESH); string mesh_format = getCacheFileEnding(fds->cache_mesh_format); string volume_format = getCacheFileEnding(fds->cache_data_format); /* Sanity check: Are cache files present? */ if (!hasMesh(fmd, framenr)) return false; ss.str(""); ss << "liquid_load_mesh_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << mesh_format << "')"; pythonCommands.push_back(ss.str()); if (mUsingMVel) { ss.str(""); ss << "liquid_load_meshvel_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "')"; pythonCommands.push_back(ss.str()); } return (mMeshFromFile = runPythonString(pythonCommands)); } bool MANTA::readParticles(FluidModifierData *fmd, int framenr, bool resumable) { if (with_debug) cout << "MANTA::readParticles()" << endl; if (!mUsingLiquid) return false; if (!mUsingDrops && !mUsingBubbles && !mUsingFloats && !mUsingTracers) return false; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_PARTICLES); string resumable_cache = (!resumable) ? "False" : "True"; /* Support older caches which had more granular file format control. */ char format = (!strcmp(fds->cache_id, FLUID_CACHE_VERSION)) ? fds->cache_data_format : fds->cache_particle_format; string volume_format = getCacheFileEnding(format); /* Sanity check: Are cache files present? */ if (!hasParticles(fmd, framenr)) return false; ss.str(""); ss << "liquid_load_particles_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); return (mParticlesFromFile = runPythonString(pythonCommands)); } bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain) { if (with_debug) cout << "MANTA::readGuiding()" << endl; if (!mUsingGuiding) return false; if (!fmd) return false; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; string directory = (sourceDomain) ? getDirectory(fmd, FLUID_DOMAIN_DIR_DATA) : getDirectory(fmd, FLUID_DOMAIN_DIR_GUIDE); string volume_format = getCacheFileEnding(fds->cache_data_format); /* Sanity check: Are cache files present? */ if (!hasGuiding(fmd, framenr, sourceDomain)) return false; if (sourceDomain) { ss.str(""); ss << "fluid_load_vel_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "')"; } else { ss.str(""); ss << "fluid_load_guiding_" << mCurrentID << "('" << escapePath(directory) << "', " << framenr << ", '" << volume_format << "')"; } pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::bakeData(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::bakeData()" << endl; string tmpString, finalString; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; char cacheDirData[FILE_MAX], cacheDirGuiding[FILE_MAX]; cacheDirData[0] = '\0'; cacheDirGuiding[0] = '\0'; string volume_format = getCacheFileEnding(fds->cache_data_format); BLI_path_join(cacheDirData, sizeof(cacheDirData), fds->cache_directory, FLUID_DOMAIN_DIR_DATA); BLI_path_join( cacheDirGuiding, sizeof(cacheDirGuiding), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE); BLI_path_make_safe(cacheDirData); BLI_path_make_safe(cacheDirGuiding); ss.str(""); ss << "bake_fluid_data_" << mCurrentID << "('" << escapePath(cacheDirData) << "', " << framenr << ", '" << volume_format << "')"; pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::bakeNoise(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::bakeNoise()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; char cacheDirNoise[FILE_MAX]; cacheDirNoise[0] = '\0'; string volume_format = getCacheFileEnding(fds->cache_data_format); BLI_path_join( cacheDirNoise, sizeof(cacheDirNoise), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE); BLI_path_make_safe(cacheDirNoise); ss.str(""); ss << "bake_noise_" << mCurrentID << "('" << escapePath(cacheDirNoise) << "', " << framenr << ", '" << volume_format << "')"; pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::bakeMesh(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::bakeMesh()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; char cacheDirMesh[FILE_MAX]; cacheDirMesh[0] = '\0'; string volume_format = getCacheFileEnding(fds->cache_data_format); string mesh_format = getCacheFileEnding(fds->cache_mesh_format); BLI_path_join(cacheDirMesh, sizeof(cacheDirMesh), fds->cache_directory, FLUID_DOMAIN_DIR_MESH); BLI_path_make_safe(cacheDirMesh); ss.str(""); ss << "bake_mesh_" << mCurrentID << "('" << escapePath(cacheDirMesh) << "', " << framenr << ", '" << volume_format << "', '" << mesh_format << "')"; pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::bakeParticles(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::bakeParticles()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; char cacheDirParticles[FILE_MAX]; cacheDirParticles[0] = '\0'; string volume_format = getCacheFileEnding(fds->cache_data_format); string resumable_cache = !(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True"; BLI_path_join(cacheDirParticles, sizeof(cacheDirParticles), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES); BLI_path_make_safe(cacheDirParticles); ss.str(""); ss << "bake_particles_" << mCurrentID << "('" << escapePath(cacheDirParticles) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::bakeGuiding(FluidModifierData *fmd, int framenr) { if (with_debug) cout << "MANTA::bakeGuiding()" << endl; ostringstream ss; vector pythonCommands; FluidDomainSettings *fds = fmd->domain; char cacheDirGuiding[FILE_MAX]; cacheDirGuiding[0] = '\0'; string volume_format = getCacheFileEnding(fds->cache_data_format); string resumable_cache = !(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True"; BLI_path_join( cacheDirGuiding, sizeof(cacheDirGuiding), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE); BLI_path_make_safe(cacheDirGuiding); ss.str(""); ss << "bake_guiding_" << mCurrentID << "('" << escapePath(cacheDirGuiding) << "', " << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); return runPythonString(pythonCommands); } bool MANTA::updateVariables(FluidModifierData *fmd) { string tmpString, finalString; vector pythonCommands; tmpString += fluid_variables; if (mUsingSmoke) tmpString += smoke_variables; if (mUsingLiquid) tmpString += liquid_variables; if (mUsingGuiding) tmpString += fluid_variables_guiding; if (mUsingNoise) { tmpString += fluid_variables_noise; tmpString += smoke_variables_noise; tmpString += smoke_wavelet_noise; } if (mUsingDrops || mUsingBubbles || mUsingFloats || mUsingTracers) { tmpString += fluid_variables_particles; tmpString += liquid_variables_particles; } if (mUsingMesh) tmpString += fluid_variables_mesh; finalString = parseScript(tmpString, fmd); pythonCommands.push_back(finalString); return runPythonString(pythonCommands); } bool MANTA::exportSmokeScript(FluidModifierData *fmd) { if (with_debug) cout << "MANTA::exportSmokeScript()" << endl; char cacheDir[FILE_MAX] = "\0"; char cacheDirScript[FILE_MAX] = "\0"; FluidDomainSettings *fds = fmd->domain; BLI_path_join(cacheDir, sizeof(cacheDir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT); BLI_path_make_safe(cacheDir); /* Create 'script' subdir if it does not exist already */ BLI_dir_create_recursive(cacheDir); BLI_path_join(cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_SMOKE_SCRIPT); BLI_path_make_safe(cacheDir); bool noise = fds->flags & FLUID_DOMAIN_USE_NOISE; bool heat = fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT; bool colors = fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS; bool fire = fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE; bool obstacle = fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; string manta_script; /* Libraries. */ manta_script += header_libraries + manta_import; /* Variables. */ manta_script += header_variables + fluid_variables + smoke_variables; if (noise) { manta_script += fluid_variables_noise + smoke_variables_noise; } if (guiding) manta_script += fluid_variables_guiding; /* Solvers. */ manta_script += header_solvers + fluid_solver; if (noise) manta_script += fluid_solver_noise; if (guiding) manta_script += fluid_solver_guiding; /* Grids. */ manta_script += header_grids + fluid_alloc + smoke_alloc; if (noise) { manta_script += smoke_alloc_noise; if (colors) manta_script += smoke_alloc_colors_noise; if (fire) manta_script += smoke_alloc_fire_noise; } if (heat) manta_script += smoke_alloc_heat; if (colors) manta_script += smoke_alloc_colors; if (fire) manta_script += smoke_alloc_fire; if (guiding) manta_script += fluid_alloc_guiding; if (obstacle) manta_script += fluid_alloc_obstacle; if (invel) manta_script += fluid_alloc_invel; if (outflow) manta_script += fluid_alloc_outflow; /* Noise field. */ if (noise) manta_script += smoke_wavelet_noise; /* Time. */ manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step; /* Import. */ manta_script += header_import + fluid_file_import + fluid_cache_helper + smoke_load_data; if (noise) manta_script += smoke_load_noise; if (guiding) manta_script += fluid_load_guiding; /* Pre/Post Steps. */ manta_script += header_prepost + fluid_pre_step + fluid_post_step; /* Steps. */ manta_script += header_steps + smoke_adaptive_step + smoke_step; if (noise) { manta_script += smoke_step_noise; } /* Main. */ manta_script += header_main + smoke_standalone + fluid_standalone; /* Fill in missing variables in script. */ string final_script = MANTA::parseScript(manta_script, fmd); /* Write script. */ ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; myfile.close(); if (!myfile) { cerr << "Fluid Error -- Could not export standalone Mantaflow smoke domain script"; return false; } return true; } bool MANTA::exportLiquidScript(FluidModifierData *fmd) { if (with_debug) cout << "MANTA::exportLiquidScript()" << endl; char cacheDir[FILE_MAX] = "\0"; char cacheDirScript[FILE_MAX] = "\0"; FluidDomainSettings *fds = fmd->domain; BLI_path_join(cacheDir, sizeof(cacheDir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT); BLI_path_make_safe(cacheDir); /* Create 'script' subdir if it does not exist already */ BLI_dir_create_recursive(cacheDir); BLI_path_join(cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_LIQUID_SCRIPT); BLI_path_make_safe(cacheDirScript); bool mesh = fds->flags & FLUID_DOMAIN_USE_MESH; bool drops = fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY; bool bubble = fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE; bool floater = fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM; bool tracer = fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER; bool obstacle = fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; bool fractions = fds->flags & FLUID_DOMAIN_USE_FRACTIONS; bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; bool viscosity = fds->flags & FLUID_DOMAIN_USE_VISCOSITY; string manta_script; /* Libraries. */ manta_script += header_libraries + manta_import; /* Variables. */ manta_script += header_variables + fluid_variables + liquid_variables; if (mesh) manta_script += fluid_variables_mesh; if (drops || bubble || floater || tracer) manta_script += fluid_variables_particles + liquid_variables_particles; if (guiding) manta_script += fluid_variables_guiding; if (viscosity) manta_script += fluid_variables_viscosity; /* Solvers. */ manta_script += header_solvers + fluid_solver; if (mesh) manta_script += fluid_solver_mesh; if (drops || bubble || floater || tracer) manta_script += fluid_solver_particles; if (guiding) manta_script += fluid_solver_guiding; if (viscosity) manta_script += fluid_solver_viscosity; /* Grids. */ manta_script += header_grids + fluid_alloc + liquid_alloc; if (mesh) manta_script += liquid_alloc_mesh; if (drops || bubble || floater || tracer) manta_script += liquid_alloc_particles; if (guiding) manta_script += fluid_alloc_guiding; if (obstacle) manta_script += fluid_alloc_obstacle; if (fractions) manta_script += fluid_alloc_fractions; if (invel) manta_script += fluid_alloc_invel; if (outflow) manta_script += fluid_alloc_outflow; if (viscosity) manta_script += liquid_alloc_viscosity; /* Domain init. */ manta_script += header_gridinit + liquid_init_phi; /* Time. */ manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step; /* Import. */ manta_script += header_import + fluid_file_import + fluid_cache_helper + liquid_load_data; if (mesh) manta_script += liquid_load_mesh; if (drops || bubble || floater || tracer) manta_script += liquid_load_particles; if (guiding) manta_script += fluid_load_guiding; /* Pre/Post Steps. */ manta_script += header_prepost + fluid_pre_step + fluid_post_step; /* Steps. */ manta_script += header_steps + liquid_adaptive_step + liquid_step; if (mesh) manta_script += liquid_step_mesh; if (drops || bubble || floater || tracer) manta_script += liquid_step_particles; /* Main. */ manta_script += header_main + liquid_standalone + fluid_standalone; /* Fill in missing variables in script. */ string final_script = MANTA::parseScript(manta_script, fmd); /* Write script. */ ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; myfile.close(); if (!myfile) { cerr << "Fluid Error -- Could not export standalone Mantaflow liquid domain script"; return false; } return true; } /* Call Mantaflow Python functions through this function. Use isAttribute for object attributes, * e.g. s.cfl (here 's' is varname, 'cfl' functionName, and isAttribute true) or * grid.getDataPointer (here 's' is varname, 'getDataPointer' functionName, and isAttribute * false) * * Important! Return value: New reference or nullptr * Caller of this function needs to handle reference count of returned object. */ static PyObject *callPythonFunction(string varName, string functionName, bool isAttribute = false) { if ((varName == "") || (functionName == "")) { if (MANTA::with_debug) cout << "Fluid: Missing Python variable name and/or function name -- name is: " << varName << ", function name is: " << functionName << endl; return nullptr; } PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *var = nullptr, *func = nullptr, *returnedValue = nullptr; /* Be sure to initialize Python before using it. */ Py_Initialize(); /* Get pyobject that holds result value. */ if (!manta_main_module) { PyGILState_Release(gilstate); return nullptr; } /* Ensure that requested variable is present in module - avoid attribute errors later on. */ if (!PyObject_HasAttrString(manta_main_module, varName.c_str())) { PyGILState_Release(gilstate); return nullptr; } var = PyObject_GetAttrString(manta_main_module, varName.c_str()); if (!var) { PyGILState_Release(gilstate); return nullptr; } func = PyObject_GetAttrString(var, functionName.c_str()); Py_DECREF(var); if (!func) { PyGILState_Release(gilstate); return nullptr; } if (!isAttribute) { returnedValue = PyObject_CallObject(func, nullptr); Py_DECREF(func); } PyGILState_Release(gilstate); return (!isAttribute) ? returnedValue : func; } /* Argument of this function may be a nullptr. * If it's not function will handle the reference count decrement of that argument. */ static void *pyObjectToPointer(PyObject *inputObject) { if (!inputObject) return nullptr; PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *encoded = PyUnicode_AsUTF8String(inputObject); char *result = PyBytes_AsString(encoded); Py_DECREF(inputObject); string str(result); istringstream in(str); void *dataPointer = nullptr; in >> dataPointer; Py_DECREF(encoded); PyGILState_Release(gilstate); return dataPointer; } /* Argument of this function may be a nullptr. * If it's not function will handle the reference count decrement of that argument. */ static double pyObjectToDouble(PyObject *inputObject) { if (!inputObject) return 0.0; PyGILState_STATE gilstate = PyGILState_Ensure(); /* Cannot use PyFloat_AsDouble() since its error check crashes. * Likely because of typedef 'Real' for 'float' types in Mantaflow. */ double result = PyFloat_AS_DOUBLE(inputObject); Py_DECREF(inputObject); PyGILState_Release(gilstate); return result; } /* Argument of this function may be a nullptr. * If it's not function will handle the reference count decrement of that argument. */ static long pyObjectToLong(PyObject *inputObject) { if (!inputObject) return 0; PyGILState_STATE gilstate = PyGILState_Ensure(); long result = PyLong_AsLong(inputObject); Py_DECREF(inputObject); PyGILState_Release(gilstate); return result; } template static T *getPointer(string pyObjectName, string pyFunctionName) { return static_cast(pyObjectToPointer(callPythonFunction(pyObjectName, pyFunctionName))); } int MANTA::getFrame() { if (with_debug) cout << "MANTA::getFrame()" << endl; string func = "frame"; string id = to_string(mCurrentID); string solver = "s" + id; return pyObjectToLong(callPythonFunction(solver, func, true)); } float MANTA::getTimestep() { if (with_debug) cout << "MANTA::getTimestep()" << endl; string func = "timestep"; string id = to_string(mCurrentID); string solver = "s" + id; return (float)pyObjectToDouble(callPythonFunction(solver, func, true)); } bool MANTA::needsRealloc(FluidModifierData *fmd) { FluidDomainSettings *fds = fmd->domain; return ((fds->res_max[0] - fds->res_min[0]) != mResX || (fds->res_max[1] - fds->res_min[1]) != mResY || (fds->res_max[2] - fds->res_min[2]) != mResZ); } void MANTA::adaptTimestep() { if (with_debug) cout << "MANTA::adaptTimestep()" << endl; vector pythonCommands; ostringstream ss; ss << "fluid_adapt_time_step_" << mCurrentID << "()"; pythonCommands.push_back(ss.str()); runPythonString(pythonCommands); } void MANTA::updatePointers(FluidModifierData *fmd, bool flush) { if (with_debug) cout << "MANTA::updatePointers()" << endl; FluidDomainSettings *fds = fmd->domain; bool liquid = !flush && (fds->type == FLUID_DOMAIN_TYPE_LIQUID); bool smoke = !flush && (fds->type == FLUID_DOMAIN_TYPE_GAS); bool noise = !flush && smoke && fds->flags & FLUID_DOMAIN_USE_NOISE; bool heat = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT; bool colors = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS; bool fire = !flush && smoke && fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE; bool obstacle = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; bool guiding = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; bool invel = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; bool outflow = !flush && fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; bool drops = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY; bool bubble = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE; bool floater = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM; bool tracer = !flush && liquid && fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER; bool parts = !flush && liquid && (drops | bubble | floater | tracer); bool mesh = !flush && liquid && fds->flags & FLUID_DOMAIN_USE_MESH; bool meshvel = !flush && liquid && mesh && fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; string func = "getDataPointer"; string funcNodes = "getNodesDataPointer"; string funcTris = "getTrisDataPointer"; string id = to_string(mCurrentID); string s_ext = "_s" + id; string pp_ext = "_pp" + id; string snd_ext = "_sp" + id; string sm_ext = "_sm" + id; string mesh_ext = "_mesh" + id; string sn_ext = "_sn" + id; mFlags = (smoke || liquid) ? getPointer("flags" + s_ext, func) : nullptr; mPhiIn = (smoke || liquid) ? getPointer("phiIn" + s_ext, func) : nullptr; mPhiStaticIn = (smoke || liquid) ? getPointer("phiSIn" + s_ext, func) : nullptr; mVelocityX = (smoke || liquid) ? getPointer("x_vel" + s_ext, func) : nullptr; mVelocityY = (smoke || liquid) ? getPointer("y_vel" + s_ext, func) : nullptr; mVelocityZ = (smoke || liquid) ? getPointer("z_vel" + s_ext, func) : nullptr; mForceX = (smoke || liquid) ? getPointer("x_force" + s_ext, func) : nullptr; mForceY = (smoke || liquid) ? getPointer("y_force" + s_ext, func) : nullptr; mForceZ = (smoke || liquid) ? getPointer("z_force" + s_ext, func) : nullptr; mPressure = (smoke || liquid) ? getPointer("pressure" + s_ext, func) : nullptr; /* Outflow. */ mPhiOutIn = (outflow) ? getPointer("phiOutIn" + s_ext, func) : nullptr; mPhiOutStaticIn = (outflow) ? getPointer("phiOutSIn" + s_ext, func) : nullptr; /* Obstacles. */ mPhiObsIn = (obstacle) ? getPointer("phiObsIn" + s_ext, func) : nullptr; mPhiObsStaticIn = (obstacle) ? getPointer("phiObsSIn" + s_ext, func) : nullptr; mObVelocityX = (obstacle) ? getPointer("x_obvel" + s_ext, func) : nullptr; mObVelocityY = (obstacle) ? getPointer("y_obvel" + s_ext, func) : nullptr; mObVelocityZ = (obstacle) ? getPointer("z_obvel" + s_ext, func) : nullptr; mNumObstacle = (obstacle) ? getPointer("numObs" + s_ext, func) : nullptr; /* Guiding. */ mPhiGuideIn = (guiding) ? getPointer("phiGuideIn" + s_ext, func) : nullptr; mGuideVelocityX = (guiding) ? getPointer("x_guidevel" + s_ext, func) : nullptr; mGuideVelocityY = (guiding) ? getPointer("y_guidevel" + s_ext, func) : nullptr; mGuideVelocityZ = (guiding) ? getPointer("z_guidevel" + s_ext, func) : nullptr; mNumGuide = (guiding) ? getPointer("numGuides" + s_ext, func) : nullptr; /* Initial velocities. */ mInVelocityX = (invel) ? getPointer("x_invel" + s_ext, func) : nullptr; mInVelocityY = (invel) ? getPointer("y_invel" + s_ext, func) : nullptr; mInVelocityZ = (invel) ? getPointer("z_invel" + s_ext, func) : nullptr; /* Smoke. */ mDensity = (smoke) ? getPointer("density" + s_ext, func) : nullptr; mDensityIn = (smoke) ? getPointer("densityIn" + s_ext, func) : nullptr; mShadow = (smoke) ? getPointer("shadow" + s_ext, func) : nullptr; mEmissionIn = (smoke) ? getPointer("emissionIn" + s_ext, func) : nullptr; /* Heat. */ mHeat = (heat) ? getPointer("heat" + s_ext, func) : nullptr; mHeatIn = (heat) ? getPointer("heatIn" + s_ext, func) : nullptr; /* Fire. */ mFlame = (fire) ? getPointer("flame" + s_ext, func) : nullptr; mFuel = (fire) ? getPointer("fuel" + s_ext, func) : nullptr; mReact = (fire) ? getPointer("react" + s_ext, func) : nullptr; mFuelIn = (fire) ? getPointer("fuelIn" + s_ext, func) : nullptr; mReactIn = (fire) ? getPointer("reactIn" + s_ext, func) : nullptr; /* Colors. */ mColorR = (colors) ? getPointer("color_r" + s_ext, func) : nullptr; mColorG = (colors) ? getPointer("color_g" + s_ext, func) : nullptr; mColorB = (colors) ? getPointer("color_b" + s_ext, func) : nullptr; mColorRIn = (colors) ? getPointer("color_r_in" + s_ext, func) : nullptr; mColorGIn = (colors) ? getPointer("color_g_in" + s_ext, func) : nullptr; mColorBIn = (colors) ? getPointer("color_b_in" + s_ext, func) : nullptr; /* Noise. */ mDensityHigh = (noise) ? getPointer("density" + sn_ext, func) : nullptr; mTextureU = (noise) ? getPointer("texture_u" + s_ext, func) : nullptr; mTextureV = (noise) ? getPointer("texture_v" + s_ext, func) : nullptr; mTextureW = (noise) ? getPointer("texture_w" + s_ext, func) : nullptr; mTextureU2 = (noise) ? getPointer("texture_u2" + s_ext, func) : nullptr; mTextureV2 = (noise) ? getPointer("texture_v2" + s_ext, func) : nullptr; mTextureW2 = (noise) ? getPointer("texture_w2" + s_ext, func) : nullptr; /* Fire with noise. */ mFlameHigh = (noise && fire) ? getPointer("flame" + sn_ext, func) : nullptr; mFuelHigh = (noise && fire) ? getPointer("fuel" + sn_ext, func) : nullptr; mReactHigh = (noise && fire) ? getPointer("react" + sn_ext, func) : nullptr; /* Colors with noise. */ mColorRHigh = (noise && colors) ? getPointer("color_r" + sn_ext, func) : nullptr; mColorGHigh = (noise && colors) ? getPointer("color_g" + sn_ext, func) : nullptr; mColorBHigh = (noise && colors) ? getPointer("color_b" + sn_ext, func) : nullptr; /* Liquid. */ mPhi = (liquid) ? getPointer("phi" + s_ext, func) : nullptr; mFlipParticleData = (liquid) ? getPointer>("pp" + s_ext, func) : nullptr; mFlipParticleVelocity = (liquid) ? getPointer>("pVel" + pp_ext, func) : nullptr; /* Mesh. */ mMeshNodes = (mesh) ? getPointer>("mesh" + sm_ext, funcNodes) : nullptr; mMeshTriangles = (mesh) ? getPointer>("mesh" + sm_ext, funcTris) : nullptr; /* Mesh velocities. */ mMeshVelocities = (meshvel) ? getPointer>("mVel" + mesh_ext, func) : nullptr; /* Secondary particles. */ mParticleData = (parts) ? getPointer>("ppSnd" + snd_ext, func) : nullptr; mParticleVelocity = (parts) ? getPointer>("pVelSnd" + pp_ext, func) : nullptr; mParticleLife = (parts) ? getPointer>("pLifeSnd" + pp_ext, func) : nullptr; mFlipFromFile = false; mMeshFromFile = false; mParticlesFromFile = false; mSmokeFromFile = false; mNoiseFromFile = false; } bool MANTA::hasConfig(FluidModifierData *fmd, int framenr) { string extension = FLUID_DOMAIN_EXTENSION_UNI; return BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, extension, framenr).c_str()); } bool MANTA::hasData(FluidModifierData *fmd, int framenr) { string extension = getCacheFileEnding(fmd->domain->cache_data_format); bool exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_DATA, extension, framenr).c_str()); /* Check single file naming. */ if (!exists) { string filename = (mUsingSmoke) ? FLUID_NAME_DENSITY : FLUID_NAME_PP; exists = BLI_exists(getFile(fmd, FLUID_DOMAIN_DIR_DATA, filename, extension, framenr).c_str()); } if (with_debug) cout << "Fluid: Has Data: " << exists << endl; return exists; } bool MANTA::hasNoise(FluidModifierData *fmd, int framenr) { string extension = getCacheFileEnding(fmd->domain->cache_data_format); bool exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_NOISE, extension, framenr).c_str()); /* Check single file naming. */ if (!exists) { extension = getCacheFileEnding(fmd->domain->cache_data_format); exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr) .c_str()); } /* Check single file naming with deprecated extension. */ if (!exists) { extension = getCacheFileEnding(fmd->domain->cache_noise_format); exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr) .c_str()); } if (with_debug) cout << "Fluid: Has Noise: " << exists << endl; return exists; } bool MANTA::hasMesh(FluidModifierData *fmd, int framenr) { string extension = getCacheFileEnding(fmd->domain->cache_mesh_format); bool exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_MESH, extension, framenr).c_str()); /* Check old file naming. */ if (!exists) { exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_LMESH, extension, framenr).c_str()); } if (with_debug) cout << "Fluid: Has Mesh: " << exists << endl; return exists; } bool MANTA::hasParticles(FluidModifierData *fmd, int framenr) { string extension = getCacheFileEnding(fmd->domain->cache_data_format); bool exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PARTICLES, extension, framenr).c_str()); /* Check single file naming. */ if (!exists) { extension = getCacheFileEnding(fmd->domain->cache_data_format); exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr) .c_str()); } /* Check single file naming with deprecated extension. */ if (!exists) { extension = getCacheFileEnding(fmd->domain->cache_particle_format); exists = BLI_exists( getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr) .c_str()); } if (with_debug) cout << "Fluid: Has Particles: " << exists << endl; return exists; } bool MANTA::hasGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain) { string subdirectory = (sourceDomain) ? FLUID_DOMAIN_DIR_DATA : FLUID_DOMAIN_DIR_GUIDE; string filename = (sourceDomain) ? FLUID_NAME_DATA : FLUID_NAME_GUIDING; string extension = getCacheFileEnding(fmd->domain->cache_data_format); bool exists = BLI_exists(getFile(fmd, subdirectory, filename, extension, framenr).c_str()); /* Check old file naming. */ if (!exists) { filename = (sourceDomain) ? FLUID_NAME_VEL : FLUID_NAME_GUIDEVEL; exists = BLI_exists(getFile(fmd, subdirectory, filename, extension, framenr).c_str()); } if (with_debug) cout << "Fluid: Has Guiding: " << exists << endl; return exists; } string MANTA::getDirectory(FluidModifierData *fmd, string subdirectory) { char directory[FILE_MAX]; BLI_path_join(directory, sizeof(directory), fmd->domain->cache_directory, subdirectory.c_str()); BLI_path_make_safe(directory); return directory; } string MANTA::getFile( FluidModifierData *fmd, string subdirectory, string fname, string extension, int framenr) { char targetFile[FILE_MAX]; string path = getDirectory(fmd, subdirectory); string filename = fname + "_####" + extension; BLI_path_join(targetFile, sizeof(targetFile), path.c_str(), filename.c_str()); BLI_path_frame(targetFile, framenr, 0); return targetFile; }