diff options
333 files changed, 63588 insertions, 451 deletions
diff --git a/.gitignore b/.gitignore index b172c446553..78ac65a63f5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,9 +26,14 @@ Desktop.ini # local patches /*.patch /*.diff - # in-source doc-gen /doc/doxygen/html/ /doc/python_api/sphinx-in-tmp/ /doc/python_api/sphinx-in/ -/doc/python_api/sphinx-out/ +/doc/python_api/sphinx-out/.gitignore +Roman_Pogribnyi/ +Roman_Pogribnyi.tar.gz +Archive.zip +Roman_Pogribnyi 2/ +make_diffs.txt +series
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e54ddc9274..6b879b45401 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,7 @@ option(WITH_GAMEENGINE "Enable Game Engine" ${_init_GAMEENGINE}) option(WITH_PLAYER "Build Player" OFF) option(WITH_OPENCOLORIO "Enable OpenColorIO color management" ${_init_OPENCOLORIO}) option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON) +option(WITH_MOD_MANTA "Enable Mantaflow Framework" ON) # GHOST Windowing Library Options option(WITH_GHOST_DEBUG "Enable debugging output for the GHOST library" OFF) @@ -2483,15 +2484,47 @@ if(WITH_PYTHON_MODULE) endif() #----------------------------------------------------------------------------- +# Configure mantaflow. +if(WITH_MOD_MANTA) + add_definitions(-DWITH_MANTA) +endif() + +#if(WITH_MOD_MANTA) +# message("including manta") +# include(ExternalProject) +# set(QT_QMAKE_EXECUTABLE /usr/bin/qmake-4.8) +# #set(BULLET_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/extern/bullet2/src") +# ExternalProject_Add( +# MantaFlow +# CVS_MODULE CMake +## CVS_TAG -r CMake-2-6-4 +# #CMAKE_ARGS -DCMAKE_INSTALL_PREFIX: +# # PATH=<INSTALL_DIR> +# #CMAKE_ARGS -DGUI=OFF +# # -DPREPDEBUG=ON +# UPDATE_COMMAND "" +# DOWNLOAD_COMMAND "" +# INSTALL_COMMAND "" +# SOURCE_DIR ${CMAKE_SOURCE_DIR}/source/blender/python/manta_pp +#) +#ExternalProject_Add_Step(MantaFlow forceconfigure +# COMMAND ${CMAKE_COMMAND} -E echo "Force configure of MantaFlow" +# DEPENDEES update +# DEPENDERS configure +# ALWAYS 0) +##set_target_properties(MantaFlow PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +#endif() + +#----------------------------------------------------------------------------- # Extra compile flags if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ALL -Wall) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_CAST_ALIGN -Wcast-align) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_DECLARATION_AFTER_STATEMENT -Werror=declaration-after-statement) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_DECLARATION_AFTER_STATEMENT -Werror=declaration-after-statement) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) # system headers sometimes do this, disable for now, was: -Werror=strict-prototypes ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_STRICT_PROTOTYPES -Wstrict-prototypes) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_MISSING_PROTOTYPES -Wmissing-prototypes) @@ -2506,7 +2539,6 @@ if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_NO_NULL -Wnonnull) # C only ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_MISSING_INCLUDE_DIRS -Wmissing-include-dirs) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_NO_DIV_BY_ZERO -Wno-div-by-zero) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_TYPE_LIMITS -Wtype-limits) # gcc 4.2 gives annoying warnings on every file with this if(NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "4.3") @@ -2533,8 +2565,7 @@ if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_INIT_SELF -Winit-self) # needs -Wuninitialized ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_MISSING_INCLUDE_DIRS -Wmissing-include-dirs) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_DIV_BY_ZERO -Wno-div-by-zero) - ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_TYPE_LIMITS -Wtype-limits) - + # gcc 4.2 gives annoying warnings on every file with this if(NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "4.3") ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_UNINITIALIZED -Wuninitialized) @@ -2562,9 +2593,9 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") # strange, clang complains these are not supported, but then yses them. ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ALL -Wall) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_DECLARATION_AFTER_STATEMENT -Werror=declaration-after-statement) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_DECLARATION_AFTER_STATEMENT -Werror=declaration-after-statement) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) +# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_NO_UNKNOWN_PRAGMAS -Wno-unknown-pragmas) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_NO_CHAR_SUBSCRIPTS -Wno-char-subscripts) diff --git a/SConstruct b/SConstruct index 00a95b0114b..a151d493a73 100644 --- a/SConstruct +++ b/SConstruct @@ -263,6 +263,7 @@ if 'blenderlite' in B.targets: target_env_defs['WITH_BF_FLUID'] = False target_env_defs['WITH_BF_OCEANSIM'] = False target_env_defs['WITH_BF_SMOKE'] = False + target_env_defs['WITH_BF_MANTA'] = False target_env_defs['WITH_BF_BOOLEAN'] = False target_env_defs['WITH_BF_REMESH'] = False target_env_defs['WITH_BF_PYTHON'] = False diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index b266602fa63..c4363b97c4a 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -510,6 +510,7 @@ macro(SETUP_BLENDER_SORTED_LIBS) bf_render bf_python bf_python_ext +# bf_python_manta # configured separately in source/creator/CMakeLists.txt bf_python_mathutils bf_python_bmesh bf_freestyle diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py index c5342d6f349..aa0631af9fe 100644 --- a/build_files/scons/tools/btools.py +++ b/build_files/scons/tools/btools.py @@ -171,6 +171,7 @@ def validate_arguments(args, bc): 'WITH_BF_REMESH', 'WITH_BF_OCEANSIM', 'WITH_BF_SMOKE', + 'WITH_BF_MANTA', 'WITH_BF_CXX_GUARDEDALLOC', 'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC', 'BUILDBOT_BRANCH', @@ -288,6 +289,7 @@ def read_opts(env, cfg, args): (BoolVariable('WITH_BF_REMESH', 'Build with remesh modifier', True)), (BoolVariable('WITH_BF_OCEANSIM', 'Build with ocean simulation', False)), (BoolVariable('WITH_BF_SMOKE', 'Build with smoke simulation', True)), + (BoolVariable('WITH_BF_MANTA', 'Build with Mantaflow Framework', True)), ('BF_PROFILE_FLAGS', 'Profiling compiler flags', ''), (BoolVariable('WITH_BF_OPENAL', 'Use OpenAL if true', False)), ('BF_OPENAL', 'Base path for OpenAL', ''), diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 110c88f8e4a..0caa5e21782 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(colamd) add_subdirectory(rangetree) add_subdirectory(wcwidth) add_subdirectory(libmv) +#add_subdirectory(manta_pp) if(WITH_BULLET) if(NOT WITH_SYSTEM_BULLET) diff --git a/extern/SConscript b/extern/SConscript index 1f94f50507f..b02e705220c 100644 --- a/extern/SConscript +++ b/extern/SConscript @@ -11,6 +11,7 @@ SConscript(['colamd/SConscript']) SConscript(['rangetree/SConscript']) SConscript(['wcwidth/SConscript']) SConscript(['libmv/SConscript']) +#SConscript(['manta_pp/SConscript']) if env['WITH_BF_GAMEENGINE']: SConscript(['recastnavigation/SConscript']) diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 0b290c617a5..57cf53af11c 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -193,6 +193,7 @@ static void makeAttribList( * Maybe a command line flag is better... */ if (getenv("BLENDER_SOFTWAREGL")) { attribs.push_back(NSOpenGLPFARendererID); + attribs.push_back(kCGLRendererGenericFloatID); } else { diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 65d371c8ca8..2914b36b9a7 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -1355,7 +1355,7 @@ GHOST_TSuccess GHOST_WindowCocoa::endProgressBar() // With OSX 10.8 and later, we can use notifications to inform the user when the progress reached 100% // Atm. just fire this when the progressbar ends, the behavior is controlled in the NotificationCenter // If Blender is not frontmost window, a message pops up with sound, in any case an entry in notifications - + if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)]) { postNotification(); } diff --git a/intern/smoke/CMakeLists.txt b/intern/smoke/CMakeLists.txt index 8cda0fd140f..54b4da4c369 100644 --- a/intern/smoke/CMakeLists.txt +++ b/intern/smoke/CMakeLists.txt @@ -26,13 +26,16 @@ set(INC intern ../memutil + ../../source/blender/blenlib ) set(INC_SYS ${BULLET_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIRS} ) +add_definitions(-DWITH_PYTHON) set(SRC intern/EIGENVALUE_HELPER.cpp @@ -59,6 +62,10 @@ set(SRC intern/VEC3.h intern/WAVELET_NOISE.h intern/WTURBULENCE.h + intern/MANTA.h + intern/MANTA.cpp + intern/scenarios/smoke.h + intern/tnt/jama_eig.h intern/tnt/jama_lu.h intern/tnt/tnt.h diff --git a/intern/smoke/extern/smoke_API.h b/intern/smoke/extern/smoke_API.h index 08dbded176e..11bb26eb6ca 100644 --- a/intern/smoke/extern/smoke_API.h +++ b/intern/smoke/extern/smoke_API.h @@ -35,10 +35,11 @@ extern "C" { #endif + struct FLUID_3D; // low res -struct FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, int use_fire, int use_colors); +struct FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, int use_fire, int use_colors, struct SmokeModifierData *smd); void smoke_free(struct FLUID_3D *fluid); void smoke_initBlenderRNA(struct FLUID_3D *fluid, float *alpha, float *beta, float *dt_factor, float *vorticity, int *border_colli, float *burning_rate, @@ -65,19 +66,20 @@ void smoke_get_ob_velocity(struct FLUID_3D *fluid, float **x, float **y, float * float *smoke_get_force_x(struct FLUID_3D *fluid); float *smoke_get_force_y(struct FLUID_3D *fluid); float *smoke_get_force_z(struct FLUID_3D *fluid); + +int *smoke_get_manta_flags(struct FLUID_3D *fluid); +float *smoke_get_inflow_grid(struct FLUID_3D *fluid); +void smoke_manta_export(struct SmokeModifierData *smd); unsigned char *smoke_get_obstacle(struct FLUID_3D *fluid); size_t smoke_get_index(int x, int max_x, int y, int max_y, int z); size_t smoke_get_index2d(int x, int max_x, int y); - void smoke_dissolve(struct FLUID_3D *fluid, int speed, int log); - // wavelet turbulence functions -struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors); +struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors,struct SmokeDomainSettings *sds); void smoke_turbulence_free(struct WTURBULENCE *wt); void smoke_turbulence_step(struct WTURBULENCE *wt, struct FLUID_3D *fluid); - float *smoke_turbulence_get_density(struct WTURBULENCE *wt); float *smoke_turbulence_get_color_r(struct WTURBULENCE *wt); float *smoke_turbulence_get_color_g(struct WTURBULENCE *wt); @@ -92,27 +94,32 @@ int smoke_turbulence_get_cells(struct WTURBULENCE *wt); void smoke_turbulence_set_noise(struct WTURBULENCE *wt, int type, const char *noisefile_path); void smoke_initWaveletBlenderRNA(struct WTURBULENCE *wt, float *strength); void smoke_dissolve_wavelet(struct WTURBULENCE *wt, int speed, int log); - /* export */ void smoke_export(struct FLUID_3D *fluid, float *dt, float *dx, float **dens, float **react, float **flame, float **fuel, float **heat, float **heatold, float **vx, float **vy, float **vz, float **r, float **g, float **b, unsigned char **obstacles); void smoke_turbulence_export(struct WTURBULENCE *wt, float **dens, float **react, float **flame, float **fuel, float **r, float **g, float **b, float **tcu, float **tcv, float **tcw); - /* flame spectrum */ void flame_get_spectrum(unsigned char *spec, int width, float t1, float t2); - /* data fields */ int smoke_has_heat(struct FLUID_3D *fluid); int smoke_has_fuel(struct FLUID_3D *fluid); int smoke_has_colors(struct FLUID_3D *fluid); int smoke_turbulence_has_fuel(struct WTURBULENCE *wt); int smoke_turbulence_has_colors(struct WTURBULENCE *wt); - void smoke_ensure_heat(struct FLUID_3D *fluid); void smoke_ensure_fire(struct FLUID_3D *fluid, struct WTURBULENCE *wt); void smoke_ensure_colors(struct FLUID_3D *fluid, struct WTURBULENCE *wt, float init_r, float init_g, float init_b); + + /*Mantaflow functions*/ +int smoke_mantaflow_read(struct SmokeDomainSettings *sds, char* name, bool with_wavelets); //1:success, 0: no file,error +void smoke_mantaflow_stop_sim(struct Manta_API *fluid); +void manta_write_effectors(struct FLUID_3D *fluid); +void manta_update_effectors(struct Scene *scene, struct Object *ob,struct SmokeDomainSettings *sds, float dt); +void manta_write_emitters(struct SmokeFlowSettings *sfs, bool highRes, int min_x, int min_y, int min_z, int max_x, int max_y, int max_z, int d_x, int d_y, int d_z,float *influence, float *vel); +void manta_export_obstacles(float * influence, int x, int y, int z); + int cell_index_3D(int index_2d,int sizex,int sizey, int sizez); /*For 2D sims only: transforms 2D manta cell index into 3D Blender index*/ #ifdef __cplusplus } #endif diff --git a/intern/smoke/intern/BASIC_FLUID.cpp b/intern/smoke/intern/BASIC_FLUID.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/intern/smoke/intern/BASIC_FLUID.cpp diff --git a/intern/smoke/intern/FLUID_3D.cpp b/intern/smoke/intern/FLUID_3D.cpp index 4faec894801..a55ab8785c8 100644 --- a/intern/smoke/intern/FLUID_3D.cpp +++ b/intern/smoke/intern/FLUID_3D.cpp @@ -35,11 +35,15 @@ #include <zlib.h> #include "float.h" - +#include "MANTA.h" +#include "scenarios/smoke.h" +#include <iostream> +#include <fstream> #if PARALLEL==1 #include <omp.h> #endif // PARALLEL +#ifndef WITH_MANTA ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// @@ -167,30 +171,6 @@ void FLUID_3D::initHeat() } } -void FLUID_3D::initFire() -{ - if (!_flame) { - _flame = new float[_totalCells]; - _fuel = new float[_totalCells]; - _fuelTemp = new float[_totalCells]; - _fuelOld = new float[_totalCells]; - _react = new float[_totalCells]; - _reactTemp = new float[_totalCells]; - _reactOld = new float[_totalCells]; - - for (int x = 0; x < _totalCells; x++) - { - _flame[x] = 0.0f; - _fuel[x] = 0.0f; - _fuelTemp[x] = 0.0f; - _fuelOld[x] = 0.0f; - _react[x] = 0.0f; - _reactTemp[x] = 0.0f; - _reactOld[x] = 0.0f; - } - } -} - void FLUID_3D::initColors(float init_r, float init_g, float init_b) { if (!_color_r) { @@ -216,47 +196,6 @@ void FLUID_3D::initColors(float init_r, float init_g, float init_b) } } -void FLUID_3D::setBorderObstacles() -{ - - // set side obstacles - unsigned int index; - for (int y = 0; y < _yRes; y++) - for (int x = 0; x < _xRes; x++) - { - // bottom slab - index = x + y * _xRes; - if(_domainBcBottom) _obstacles[index] = 1; - - // top slab - index += _totalCells - _slabSize; - if(_domainBcTop) _obstacles[index] = 1; - } - - for (int z = 0; z < _zRes; z++) - for (int x = 0; x < _xRes; x++) - { - // front slab - index = x + z * _slabSize; - if(_domainBcFront) _obstacles[index] = 1; - - // back slab - index += _slabSize - _xRes; - if(_domainBcBack) _obstacles[index] = 1; - } - - for (int z = 0; z < _zRes; z++) - for (int y = 0; y < _yRes; y++) - { - // left slab - index = y * _xRes + z * _slabSize; - if(_domainBcLeft) _obstacles[index] = 1; - - // right slab - index += _xRes - 1; - if(_domainBcRight) _obstacles[index] = 1; - } -} FLUID_3D::~FLUID_3D() { @@ -514,14 +453,355 @@ void FLUID_3D::step(float dt, float gravity[3]) } +/*===============================================================================================*/ +/*===============================================================================================*/ +#else /*USING MANTAFLOW STRUCTURES*/ +/*===============================================================================================*/ +/*===============================================================================================*/ -// Set border collision model from RNA setting +FLUID_3D::FLUID_3D(int *res, float dx, float dtdef, int init_heat, int init_fire, int init_colors, SmokeModifierData *smd) : +_xRes(res[0]), _yRes(res[1]), _zRes(res[2]), _res(0.0f) +{ + // set simulation consts + _dt = dtdef; // just in case. set in step from a RNA factor + + _iterations = 100; + _tempAmb = 0; + _heatDiffusion = 1e-3; + _totalTime = 0.0f; + _totalSteps = 0; + _res = Vec3Int(_xRes,_yRes,_zRes); + _maxRes = MAX3(_xRes, _yRes, _zRes); + + // initialize wavelet turbulence + /* + if(amplify) + _wTurbulence = new WTURBULENCE(_res[0],_res[1],_res[2], amplify, noisetype); + else + _wTurbulence = NULL; + */ + + // scale the constants according to the refinement of the grid + if (!dx) + _dx = 1.0f / (float)_maxRes; + else + _dx = dx; + _constantScaling = 64.0f / _maxRes; + _constantScaling = (_constantScaling < 1.0f) ? 1.0f : _constantScaling; + _vorticityEps = 2.0f / _constantScaling; // Just in case set a default value + + // allocate arrays + _totalCells = _xRes * _yRes * _zRes; + _slabSize = _xRes * _yRes; + _xVelocity = new float[_totalCells]; + _yVelocity = new float[_totalCells]; + _zVelocity = new float[_totalCells]; + _xVelocityOb = new float[_totalCells]; + _yVelocityOb = new float[_totalCells]; + _zVelocityOb = new float[_totalCells]; + _xVelocityOld = new float[_totalCells]; + _yVelocityOld = new float[_totalCells]; + _zVelocityOld = new float[_totalCells]; + _xForce = new float[_totalCells]; + _yForce = new float[_totalCells]; + _zForce = new float[_totalCells]; + /*if two-dimentional, insert manta sim into blender _density field */ + if (smd->domain->manta_solver_res == 2){ + _density = new float[_totalCells]; + _manta_flags = new int[_totalCells]; + manta_resoution = 2; + _yLocation = _yRes / 2; + } + else{ + _density = NULL; + _manta_flags = NULL; + manta_resoution = 3; + _yLocation = -1; + } + _manta_inflow = NULL; + _densityOld = new float[_totalCells]; + _obstacles = new unsigned char[_totalCells]; // set 0 at end of step + + // For threaded version: + _xVelocityTemp = new float[_totalCells]; + _yVelocityTemp = new float[_totalCells]; + _zVelocityTemp = new float[_totalCells]; + _densityTemp = new float[_totalCells]; + + //initializing manta flag grids + // DG TODO: check if alloc went fine + + for (int x = 0; x < _totalCells; x++) + { + _densityOld[x] = 0.0f; + _xVelocity[x] = 0.0f; + _yVelocity[x] = 0.0f; + _zVelocity[x] = 0.0f; + _xVelocityOb[x] = 0.0f; + _yVelocityOb[x] = 0.0f; + _zVelocityOb[x] = 0.0f; + _xVelocityOld[x] = 0.0f; + _yVelocityOld[x] = 0.0f; + _zVelocityOld[x] = 0.0f; + _xForce[x] = 0.0f; + _yForce[x] = 0.0f; + _zForce[x] = 0.0f; + _obstacles[x] = false; + } + + /* heat */ + _heat = _heatOld = _heatTemp = NULL; + using_heat = false; + if (init_heat) { + initHeat(); + } + // Fire simulation + _flame = _fuel = _fuelTemp = _fuelOld = NULL; + _react = _reactTemp = _reactOld = NULL; + if (init_fire) { + initFire(); + } + // Smoke color + _color_r = _color_rOld = _color_rTemp = NULL; + _color_g = _color_gOld = _color_gTemp = NULL; + _color_b = _color_bOld = _color_bTemp = NULL; + using_colors = false; + if (init_colors) { + initColors(0.0f, 0.0f, 0.0f); + } + + // boundary conditions of the fluid domain + // set default values -> vertically non-colliding + _domainBcFront = true; + _domainBcTop = false; + _domainBcLeft = true; + _domainBcBack = _domainBcFront; + _domainBcBottom = _domainBcTop; + _domainBcRight = _domainBcLeft; + + _colloPrev = 1; // default value + + string smoke_script = ""; + if (smd->domain->flags & MOD_SMOKE_MANTA_USE_LIQUID) + smoke_script = smoke_setup_low + liquid_step_low; + else + smoke_script = smoke_setup_low + smoke_step_low; + smd->domain->fluid = this; + std::string final_script = Manta_API::parseScript(smoke_script, smd); + ofstream myfile; + cout<< "INITIALIZING SMOKE" << endl; + myfile.open ("manta_scene.py"); + myfile << final_script; + myfile.close(); + vector<string> a; + a.push_back("manta_scene.py"); + runMantaScript(final_script,a); /*need this to delete previous solvers and grids*/ + Manta_API::updatePointers(this, using_colors); +} -void FLUID_3D::setBorderCollisions() { +void FLUID_3D::initHeat() +{ + if (!_heat) { + _heat = NULL; + _heatOld = new float[_totalCells]; + _heatTemp = new float[_totalCells]; + + for (int x = 0; x < _totalCells; x++) + { + _heatOld[x] = 0.0f; + } + using_heat = true; + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString(smoke_init_heat_low.c_str()); + PyGILState_Release(gilstate); + Manta_API::updatePointers(this, using_colors); + } +} - _colloPrev = *_borderColli; // saving the current value +void FLUID_3D::initColors(float init_r, float init_g, float init_b) +{ + if (!_color_r){ + using_colors = true; + PyGILState_STATE gilstate = PyGILState_Ensure(); + stringstream ss; + ss << "manta_color_r = " << init_r << endl; + ss << "manta_color_g = " << init_g << endl; + ss << "manta_color_b = " << init_b << endl; + PyRun_SimpleString(ss.str().c_str()); + PyRun_SimpleString(smoke_init_colors_low.c_str()); + PyGILState_Release(gilstate); + Manta_API::updatePointers(this, true); + } +} + + +FLUID_3D::~FLUID_3D() +{ + if (_xVelocity) delete[] _xVelocity; + if (_yVelocity) delete[] _yVelocity; + if (_zVelocity) delete[] _zVelocity; + if (_xVelocityOb) delete[] _xVelocityOb; + if (_yVelocityOb) delete[] _yVelocityOb; + if (_zVelocityOb) delete[] _zVelocityOb; + if (_xVelocityOld) delete[] _xVelocityOld; + if (_yVelocityOld) delete[] _yVelocityOld; + if (_zVelocityOld) delete[] _zVelocityOld; + if (_xForce) delete[] _xForce; + if (_yForce) delete[] _yForce; + if (_zForce) delete[] _zForce; + if (_densityOld) delete[] _densityOld; + if (_heatOld) delete[] _heatOld; + if (_obstacles) delete[] _obstacles; + + if (_xVelocityTemp) delete[] _xVelocityTemp; + if (_yVelocityTemp) delete[] _yVelocityTemp; + if (_zVelocityTemp) delete[] _zVelocityTemp; + if (_densityTemp) delete[] _densityTemp; + if (_heatTemp) delete[] _heatTemp; + + if (_flame) delete[] _flame; + if (_fuel) delete[] _fuel; + if (_fuelTemp) delete[] _fuelTemp; + if (_fuelOld) delete[] _fuelOld; + if (_react) delete[] _react; + if (_reactTemp) delete[] _reactTemp; + if (_reactOld) delete[] _reactOld; + + if (_color_r) delete[] _color_r; + if (_color_rOld) delete[] _color_rOld; + if (_color_rTemp) delete[] _color_rTemp; + if (_color_g) delete[] _color_g; + if (_color_gOld) delete[] _color_gOld; + if (_color_gTemp) delete[] _color_gTemp; + if (_color_b) delete[] _color_b; + if (_color_bOld) delete[] _color_bOld; + if (_color_bTemp) delete[] _color_bTemp; + + // printf("deleted fluid\n"); +} + +// init direct access functions from blender +void FLUID_3D::initBlenderRNA(float *alpha, float *beta, float *dt_factor, float *vorticity, int *borderCollision, float *burning_rate, + float *flame_smoke, float *flame_smoke_color, float *flame_vorticity, float *flame_ignition_temp, float *flame_max_temp) +{ + _alpha = alpha; + _beta = beta; + _dtFactor = dt_factor; + _vorticityRNA = vorticity; + _borderColli = borderCollision; + _burning_rate = burning_rate; + _flame_smoke = flame_smoke; + _flame_smoke_color = flame_smoke_color; + _flame_vorticity = flame_vorticity; + _ignition_temp = flame_ignition_temp; + _max_temp = flame_max_temp; +} + +////////////////////////////////////////////////////////////////////// +// step simulation once +////////////////////////////////////////////////////////////////////// +void FLUID_3D::step(float dt, float gravity[3]) +{ + // BLender computes heat buoyancy, not yet impl. in Manta + manta_write_effectors(this); + Manta_API::updatePointers(this,using_colors); +// diffuseHeat(); + + int sim_frame = 1; + PyGILState_STATE gilstate = PyGILState_Ensure(); + std::string frame_str = static_cast<ostringstream*>( &(ostringstream() << sim_frame) )->str(); + std::string py_string_0 = string("sim_step_low(").append(frame_str); + std::string py_string_1 = py_string_0.append(")\0"); + PyRun_SimpleString(py_string_1.c_str()); + cout<< "done"<<manta_sim_running<<endl; + PyGILState_Release(gilstate); + Manta_API::updatePointers(this,using_colors); + for (int i = 0; i < _totalCells; i++) + { + _xForce[i] = _yForce[i] = _zForce[i] = 0.0f; + } + +} +#endif /*WITH_MANTA*/ + +void FLUID_3D::initFire() +{ + if (!_flame) { + _flame = new float[_totalCells]; + _fuel = new float[_totalCells]; + _fuelTemp = new float[_totalCells]; + _fuelOld = new float[_totalCells]; + _react = new float[_totalCells]; + _reactTemp = new float[_totalCells]; + _reactOld = new float[_totalCells]; + + for (int x = 0; x < _totalCells; x++) + { + _flame[x] = 0.0f; + _fuel[x] = 0.0f; + _fuelTemp[x] = 0.0f; + _fuelOld[x] = 0.0f; + _react[x] = 0.0f; + _reactTemp[x] = 0.0f; + _reactOld[x] = 0.0f; + } + } +} + + +void FLUID_3D::setBorderObstacles() +{ + + // set side obstacles + unsigned int index; + for (int y = 0; y < _yRes; y++) + for (int x = 0; x < _xRes; x++) + { + // bottom slab + index = x + y * _xRes; + if(_domainBcBottom) _obstacles[index] = 1; + + // top slab + index += _totalCells - _slabSize; + if(_domainBcTop) _obstacles[index] = 1; + } + + for (int z = 0; z < _zRes; z++) + for (int x = 0; x < _xRes; x++) + { + // front slab + index = x + z * _slabSize; + if(_domainBcFront) _obstacles[index] = 1; + + // back slab + index += _slabSize - _xRes; + if(_domainBcBack) _obstacles[index] = 1; + } + + for (int z = 0; z < _zRes; z++) + for (int y = 0; y < _yRes; y++) + { + // left slab + index = y * _xRes + z * _slabSize; + if(_domainBcLeft) _obstacles[index] = 1; + + // right slab + index += _xRes - 1; + if(_domainBcRight) _obstacles[index] = 1; + } +} + + + +// Set border collision model from RNA setting + +void FLUID_3D::setBorderCollisions() { + + + _colloPrev = *_borderColli; // saving the current value + // boundary conditions of the fluid domain if (_colloPrev == 0) { @@ -544,13 +824,13 @@ void FLUID_3D::setBorderCollisions() { _domainBcTop = false; _domainBcLeft = true; } - + _domainBcBack = _domainBcFront; _domainBcBottom = _domainBcTop; _domainBcRight = _domainBcLeft; - - - + + + // set side obstacles setBorderObstacles(); } @@ -563,55 +843,55 @@ void FLUID_3D::setBorderCollisions() { void FLUID_3D::artificialDampingSL(int zBegin, int zEnd) { const float w = 0.9; - + memmove(_xForce+(_slabSize*zBegin), _xVelocityTemp+(_slabSize*zBegin), sizeof(float)*_slabSize*(zEnd-zBegin)); memmove(_yForce+(_slabSize*zBegin), _yVelocityTemp+(_slabSize*zBegin), sizeof(float)*_slabSize*(zEnd-zBegin)); memmove(_zForce+(_slabSize*zBegin), _zVelocityTemp+(_slabSize*zBegin), sizeof(float)*_slabSize*(zEnd-zBegin)); - - + + if(_totalSteps % 4 == 1) { for (int z = zBegin+1; z < zEnd-1; z++) for (int y = 1; y < _res[1]-1; y++) for (int x = 1+(y+z)%2; x < _res[0]-1; x+=2) { const int index = x + y*_res[0] + z * _slabSize; _xForce[index] = (1-w)*_xVelocityTemp[index] + 1.0f/6.0f * w*( - _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + - _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + - _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); - + _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + + _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + + _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); + _yForce[index] = (1-w)*_yVelocityTemp[index] + 1.0f/6.0f * w*( - _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + - _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + - _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); - + _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + + _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + + _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); + _zForce[index] = (1-w)*_zVelocityTemp[index] + 1.0f/6.0f * w*( - _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + - _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + - _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); + _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + + _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + + _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); } } - + if(_totalSteps % 4 == 3) { for (int z = zBegin+1; z < zEnd-1; z++) for (int y = 1; y < _res[1]-1; y++) for (int x = 1+(y+z+1)%2; x < _res[0]-1; x+=2) { const int index = x + y*_res[0] + z * _slabSize; _xForce[index] = (1-w)*_xVelocityTemp[index] + 1.0f/6.0f * w*( - _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + - _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + - _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); - + _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + + _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + + _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); + _yForce[index] = (1-w)*_yVelocityTemp[index] + 1.0f/6.0f * w*( - _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + - _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + - _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); - + _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + + _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + + _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); + _zForce[index] = (1-w)*_zVelocityTemp[index] + 1.0f/6.0f * w*( - _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + - _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + - _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); + _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + + _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + + _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); } - + } } @@ -621,66 +901,66 @@ void FLUID_3D::artificialDampingExactSL(int pos) { const float w = 0.9; int index, x,y,z; - + size_t posslab; - + for (z=pos-1; z<=pos; z++) { - posslab=z * _slabSize; - - if(_totalSteps % 4 == 1) { + posslab=z * _slabSize; + + if(_totalSteps % 4 == 1) { for (y = 1; y < _res[1]-1; y++) for (x = 1+(y+z)%2; x < _res[0]-1; x+=2) { index = x + y*_res[0] + posslab; /* - * Uses xForce as temporary storage to allow other threads to read - * old values from xVelocityTemp - */ + * Uses xForce as temporary storage to allow other threads to read + * old values from xVelocityTemp + */ _xForce[index] = (1-w)*_xVelocityTemp[index] + 1.0f/6.0f * w*( - _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + - _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + - _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); - + _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + + _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + + _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); + _yForce[index] = (1-w)*_yVelocityTemp[index] + 1.0f/6.0f * w*( - _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + - _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + - _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); - + _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + + _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + + _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); + _zForce[index] = (1-w)*_zVelocityTemp[index] + 1.0f/6.0f * w*( - _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + - _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + - _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); + _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + + _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + + _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); } - } - - if(_totalSteps % 4 == 3) { + } + + if(_totalSteps % 4 == 3) { for (y = 1; y < _res[1]-1; y++) for (x = 1+(y+z+1)%2; x < _res[0]-1; x+=2) { index = x + y*_res[0] + posslab; - + /* - * Uses xForce as temporary storage to allow other threads to read - * old values from xVelocityTemp - */ + * Uses xForce as temporary storage to allow other threads to read + * old values from xVelocityTemp + */ _xForce[index] = (1-w)*_xVelocityTemp[index] + 1.0f/6.0f * w*( - _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + - _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + - _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); - + _xVelocityTemp[index+1] + _xVelocityTemp[index-1] + + _xVelocityTemp[index+_res[0]] + _xVelocityTemp[index-_res[0]] + + _xVelocityTemp[index+_slabSize] + _xVelocityTemp[index-_slabSize] ); + _yForce[index] = (1-w)*_yVelocityTemp[index] + 1.0f/6.0f * w*( - _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + - _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + - _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); - + _yVelocityTemp[index+1] + _yVelocityTemp[index-1] + + _yVelocityTemp[index+_res[0]] + _yVelocityTemp[index-_res[0]] + + _yVelocityTemp[index+_slabSize] + _yVelocityTemp[index-_slabSize] ); + _zForce[index] = (1-w)*_zVelocityTemp[index] + 1.0f/6.0f * w*( - _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + - _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + - _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); + _zVelocityTemp[index+1] + _zVelocityTemp[index-1] + + _zVelocityTemp[index+_res[0]] + _zVelocityTemp[index-_res[0]] + + _zVelocityTemp[index+_slabSize] + _zVelocityTemp[index-_slabSize] ); } - - } + + } } } @@ -692,45 +972,45 @@ void FLUID_3D::copyBorderAll(float* field, int zBegin, int zEnd) int index, x, y, z; int zSize = zEnd-zBegin; int _blockTotalCells=_slabSize * zSize; - + if (zBegin==0) - for (int y = 0; y < _yRes; y++) - for (int x = 0; x < _xRes; x++) - { - // front slab - index = x + y * _xRes; - field[index] = field[index + _slabSize]; - } - + for (int y = 0; y < _yRes; y++) + for (int x = 0; x < _xRes; x++) + { + // front slab + index = x + y * _xRes; + field[index] = field[index + _slabSize]; + } + if (zEnd==_zRes) - for (y = 0; y < _yRes; y++) - for (x = 0; x < _xRes; x++) - { - - // back slab - index = x + y * _xRes + _blockTotalCells - _slabSize; - field[index] = field[index - _slabSize]; - } - + for (y = 0; y < _yRes; y++) + for (x = 0; x < _xRes; x++) + { + + // back slab + index = x + y * _xRes + _blockTotalCells - _slabSize; + field[index] = field[index - _slabSize]; + } + for (z = 0; z < zSize; z++) for (x = 0; x < _xRes; x++) - { + { // bottom slab index = x + z * _slabSize; field[index] = field[index + _xRes]; - + // top slab index += _slabSize - _xRes; field[index] = field[index - _xRes]; - } - + } + for (z = 0; z < zSize; z++) for (y = 0; y < _yRes; y++) - { + { // left slab index = y * _xRes + z * _slabSize; field[index] = field[index + 1]; - + // right slab index += _xRes - 1; field[index] = field[index - 1]; @@ -763,14 +1043,14 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) ///////////////////////////////////// // setZeroBorder to all: ///////////////////////////////////// - + ///////////////////////////////////// // setZeroX ///////////////////////////////////// - + const int slabSize = _xRes * _yRes; int index, x,y,z; - + for (z = zBegin; z < zEnd; z++) for (y = 0; y < _yRes; y++) { @@ -789,7 +1069,7 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) _color_g[index] = 0.0f; _color_b[index] = 0.0f; } - + // right slab index += _xRes - 1; _xVelocity[index] = 0.0f; @@ -806,11 +1086,11 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) _color_b[index] = 0.0f; } } - + ///////////////////////////////////// // setZeroY ///////////////////////////////////// - + for (z = zBegin; z < zEnd; z++) for (x = 0; x < _xRes; x++) { @@ -829,7 +1109,7 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) _color_g[index] = 0.0f; _color_b[index] = 0.0f; } - + // top slab index += slabSize - _xRes; _xVelocity[index] = 0.0f; @@ -845,47 +1125,47 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) _color_g[index] = 0.0f; _color_b[index] = 0.0f; } - + } - + ///////////////////////////////////// // setZeroZ ///////////////////////////////////// - - + + const int totalCells = _xRes * _yRes * _zRes; - + index = 0; if (zBegin == 0) - for (y = 0; y < _yRes; y++) - for (x = 0; x < _xRes; x++, index++) - { - // front slab - _xVelocity[index] = 0.0f; - _yVelocity[index] = 0.0f; - _zVelocity[index] = 0.0f; - _density[index] = 0.0f; - if (_fuel) { - _fuel[index] = 0.0f; - _react[index] = 0.0f; - } - if (_color_r) { - _color_r[index] = 0.0f; - _color_g[index] = 0.0f; - _color_b[index] = 0.0f; + for (y = 0; y < _yRes; y++) + for (x = 0; x < _xRes; x++, index++) + { + // front slab + _xVelocity[index] = 0.0f; + _yVelocity[index] = 0.0f; + _zVelocity[index] = 0.0f; + _density[index] = 0.0f; + if (_fuel) { + _fuel[index] = 0.0f; + _react[index] = 0.0f; + } + if (_color_r) { + _color_r[index] = 0.0f; + _color_g[index] = 0.0f; + _color_b[index] = 0.0f; + } } - } - + if (zEnd == _zRes) { index=0; int index_top=0; const int cellsslab = totalCells - slabSize; - + for (y = 0; y < _yRes; y++) for (x = 0; x < _xRes; x++, index++) { - + // back slab index_top = index + cellsslab; _xVelocity[index_top] = 0.0f; @@ -903,7 +1183,7 @@ void FLUID_3D::wipeBoundariesSL(int zBegin, int zEnd) } } } - + } ////////////////////////////////////////////////////////////////////// // add forces to velocity field @@ -912,7 +1192,7 @@ void FLUID_3D::addForce(int zBegin, int zEnd) { int begin=zBegin * _slabSize; int end=begin + (zEnd - zBegin) * _slabSize; - + for (int i = begin; i < end; i++) { _xVelocityTemp[i] = _xVelocity[i] + _dt * _xForce[i]; @@ -927,10 +1207,10 @@ void FLUID_3D::project() { int x, y, z; size_t index; - + float *_pressure = new float[_totalCells]; float *_divergence = new float[_totalCells]; - + memset(_pressure, 0, sizeof(float)*_totalCells); memset(_divergence, 0, sizeof(float)*_totalCells); @@ -940,13 +1220,13 @@ void FLUID_3D::project() // copy out the boundaries if(!_domainBcLeft) setNeumannX(_xVelocity, _res, 0, _zRes); else setZeroX(_xVelocity, _res, 0, _zRes); - + if(!_domainBcFront) setNeumannY(_yVelocity, _res, 0, _zRes); else setZeroY(_yVelocity, _res, 0, _zRes); - + if(!_domainBcTop) setNeumannZ(_zVelocity, _res, 0, _zRes); else setZeroZ(_zVelocity, _res, 0, _zRes); - + // calculate divergence index = _slabSize + _xRes + 1; for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes) @@ -960,44 +1240,44 @@ void FLUID_3D::project() continue; } - + float xright = _xVelocity[index + 1]; float xleft = _xVelocity[index - 1]; float yup = _yVelocity[index + _xRes]; float ydown = _yVelocity[index - _xRes]; float ztop = _zVelocity[index + _slabSize]; float zbottom = _zVelocity[index - _slabSize]; - + if(_obstacles[index+1]) xright = - _xVelocity[index]; // DG: += if(_obstacles[index-1]) xleft = - _xVelocity[index]; if(_obstacles[index+_xRes]) yup = - _yVelocity[index]; if(_obstacles[index-_xRes]) ydown = - _yVelocity[index]; if(_obstacles[index+_slabSize]) ztop = - _zVelocity[index]; if(_obstacles[index-_slabSize]) zbottom = - _zVelocity[index]; - + if(_obstacles[index+1] & 8) xright += _xVelocityOb[index + 1]; if(_obstacles[index-1] & 8) xleft += _xVelocityOb[index - 1]; if(_obstacles[index+_xRes] & 8) yup += _yVelocityOb[index + _xRes]; if(_obstacles[index-_xRes] & 8) ydown += _yVelocityOb[index - _xRes]; if(_obstacles[index+_slabSize] & 8) ztop += _zVelocityOb[index + _slabSize]; if(_obstacles[index-_slabSize] & 8) zbottom += _zVelocityOb[index - _slabSize]; - + _divergence[index] = -_dx * 0.5f * ( - xright - xleft + - yup - ydown + - ztop - zbottom ); - + xright - xleft + + yup - ydown + + ztop - zbottom ); + // Pressure is zero anyway since now a local array is used _pressure[index] = 0.0f; } - + copyBorderAll(_pressure, 0, _zRes); - + // solve Poisson equation solvePressurePre(_pressure, _divergence, _obstacles); - + setObstaclePressure(_pressure, 0, _zRes); - + // project out solution // New idea for code from NVIDIA graphic gems 3 - DG float invDx = 1.0f / _dx; @@ -1008,7 +1288,7 @@ void FLUID_3D::project() { float vMask[3] = {1.0f, 1.0f, 1.0f}, vObst[3] = {0, 0, 0}; // float vR = 0.0f, vL = 0.0f, vT = 0.0f, vB = 0.0f, vD = 0.0f, vU = 0.0f; // UNUSED - + float pC = _pressure[index]; // center float pR = _pressure[index + 1]; // right float pL = _pressure[index - 1]; // left @@ -1016,7 +1296,7 @@ void FLUID_3D::project() float pD = _pressure[index - _xRes]; // Down float pT = _pressure[index + _slabSize]; // top float pB = _pressure[index - _slabSize]; // bottom - + if(!_obstacles[index]) { // DG TODO: What if obstacle is left + right and one of them is moving? @@ -1026,11 +1306,11 @@ void FLUID_3D::project() if(_obstacles[index-_xRes]) { pD = pC; vObst[1] = _yVelocityOb[index - _xRes]; vMask[1] = 0; } if(_obstacles[index+_slabSize]) { pT = pC; vObst[2] = _zVelocityOb[index + _slabSize]; vMask[2] = 0; } if(_obstacles[index-_slabSize]) { pB = pC; vObst[2] = _zVelocityOb[index - _slabSize]; vMask[2] = 0; } - + _xVelocity[index] -= 0.5f * (pR - pL) * invDx; _yVelocity[index] -= 0.5f * (pU - pD) * invDx; _zVelocity[index] -= 0.5f * (pT - pB) * invDx; - + _xVelocity[index] = (vMask[0] * _xVelocity[index]) + vObst[0]; _yVelocity[index] = (vMask[1] * _yVelocity[index]) + vObst[1]; _zVelocity[index] = (vMask[2] * _zVelocity[index]) + vObst[2]; @@ -1042,10 +1322,10 @@ void FLUID_3D::project() _zVelocity[index] = _zVelocityOb[index]; } } - + // DG: was enabled in original code but now we do this later // setObstacleVelocity(0, _zRes); - + if (_pressure) delete[] _pressure; if (_divergence) delete[] _divergence; } @@ -1057,57 +1337,57 @@ void FLUID_3D::setObstacleVelocity(int zBegin, int zEnd) { // completely TODO <-- who wrote this and what is here TODO? DG - + const size_t index_ = _slabSize + _xRes + 1; - + //int vIndex=_slabSize + _xRes + 1; - + int bb=0; int bt=0; - + if (zBegin == 0) {bb = 1;} if (zEnd == _zRes) {bt = 1;} - + // tag remaining obstacle blocks for (int z = zBegin + bb; z < zEnd - bt; z++) { size_t index = index_ +(z-1)*_slabSize; - + for (int y = 1; y < _yRes - 1; y++, index += 2) { for (int x = 1; x < _xRes - 1; x++, index++) - { - if (!_obstacles[index]) { - // if(_obstacles[index+1]) xright = - _xVelocityOb[index]; - if((_obstacles[index - 1] & 8) && abs(_xVelocityOb[index - 1]) > FLT_EPSILON ) - { - // printf("velocity x!\n"); - _xVelocity[index] = _xVelocityOb[index - 1]; - _xVelocity[index - 1] = _xVelocityOb[index - 1]; - } - // if(_obstacles[index+_xRes]) yup = - _yVelocityOb[index]; - if((_obstacles[index - _xRes] & 8) && abs(_yVelocityOb[index - _xRes]) > FLT_EPSILON) + if (!_obstacles[index]) { - // printf("velocity y!\n"); - _yVelocity[index] = _yVelocityOb[index - _xRes]; - _yVelocity[index - _xRes] = _yVelocityOb[index - _xRes]; + // if(_obstacles[index+1]) xright = - _xVelocityOb[index]; + if((_obstacles[index - 1] & 8) && abs(_xVelocityOb[index - 1]) > FLT_EPSILON ) + { + // printf("velocity x!\n"); + _xVelocity[index] = _xVelocityOb[index - 1]; + _xVelocity[index - 1] = _xVelocityOb[index - 1]; + } + // if(_obstacles[index+_xRes]) yup = - _yVelocityOb[index]; + if((_obstacles[index - _xRes] & 8) && abs(_yVelocityOb[index - _xRes]) > FLT_EPSILON) + { + // printf("velocity y!\n"); + _yVelocity[index] = _yVelocityOb[index - _xRes]; + _yVelocity[index - _xRes] = _yVelocityOb[index - _xRes]; + } + // if(_obstacles[index+_slabSize]) ztop = - _zVelocityOb[index]; + if((_obstacles[index - _slabSize] & 8) && abs(_zVelocityOb[index - _slabSize]) > FLT_EPSILON) + { + // printf("velocity z!\n"); + _zVelocity[index] = _zVelocityOb[index - _slabSize]; + _zVelocity[index - _slabSize] = _zVelocityOb[index - _slabSize]; + } } - // if(_obstacles[index+_slabSize]) ztop = - _zVelocityOb[index]; - if((_obstacles[index - _slabSize] & 8) && abs(_zVelocityOb[index - _slabSize]) > FLT_EPSILON) + else { - // printf("velocity z!\n"); - _zVelocity[index] = _zVelocityOb[index - _slabSize]; - _zVelocity[index - _slabSize] = _zVelocityOb[index - _slabSize]; + _density[index] = 0; } - } - else - { - _density[index] = 0; - } - //vIndex++; - } // x loop - //vIndex += 2; + //vIndex++; + } // x loop + //vIndex += 2; } // y loop //vIndex += 2 * _xRes; } // z loop @@ -1119,10 +1399,10 @@ void FLUID_3D::setObstacleVelocity(int zBegin, int zEnd) void FLUID_3D::diffuseHeat() { SWAP_POINTERS(_heat, _heatOld); - + copyBorderAll(_heatOld, 0, _zRes); solveHeat(_heat, _heatOld, _obstacles); - + // zero out inside obstacles for (int x = 0; x < _totalCells; x++) if (_obstacles[x]) @@ -1140,7 +1420,7 @@ void FLUID_3D::addObstacle(OBSTACLE* obstacle) for (int x = 0; x < _xRes; x++, index++) if (obstacle->inside(x * _dx, y * _dx, z * _dx)) { _obstacles[index] = true; - } + } } ////////////////////////////////////////////////////////////////////// @@ -1148,88 +1428,88 @@ void FLUID_3D::addObstacle(OBSTACLE* obstacle) ////////////////////////////////////////////////////////////////////// void FLUID_3D::setObstaclePressure(float *_pressure, int zBegin, int zEnd) { - + // completely TODO <-- who wrote this and what is here TODO? DG - + const size_t index_ = _slabSize + _xRes + 1; - + //int vIndex=_slabSize + _xRes + 1; - + int bb=0; int bt=0; - + if (zBegin == 0) {bb = 1;} if (zEnd == _zRes) {bt = 1;} - + // tag remaining obstacle blocks for (int z = zBegin + bb; z < zEnd - bt; z++) { size_t index = index_ +(z-1)*_slabSize; - + for (int y = 1; y < _yRes - 1; y++, index += 2) { for (int x = 1; x < _xRes - 1; x++, index++) - { - // could do cascade of ifs, but they are a pain - if (_obstacles[index] /* && !(_obstacles[index] & 8) DG TODO TEST THIS CONDITION */) { - const int top = _obstacles[index + _slabSize]; - const int bottom= _obstacles[index - _slabSize]; - const int up = _obstacles[index + _xRes]; - const int down = _obstacles[index - _xRes]; - const int left = _obstacles[index - 1]; - const int right = _obstacles[index + 1]; - - // unused - // const bool fullz = (top && bottom); - // const bool fully = (up && down); - //const bool fullx = (left && right); - - /* - _xVelocity[index] = - _yVelocity[index] = - _zVelocity[index] = 0.0f; - */ - _pressure[index] = 0.0f; - - // average pressure neighbors - float pcnt = 0.; - if (left && !right) { - _pressure[index] += _pressure[index + 1]; - pcnt += 1.0f; - } - if (!left && right) { - _pressure[index] += _pressure[index - 1]; - pcnt += 1.0f; - } - if (up && !down) { - _pressure[index] += _pressure[index - _xRes]; - pcnt += 1.0f; - } - if (!up && down) { - _pressure[index] += _pressure[index + _xRes]; - pcnt += 1.0f; - } - if (top && !bottom) { - _pressure[index] += _pressure[index - _slabSize]; - pcnt += 1.0f; - } - if (!top && bottom) { - _pressure[index] += _pressure[index + _slabSize]; - pcnt += 1.0f; + // could do cascade of ifs, but they are a pain + if (_obstacles[index] /* && !(_obstacles[index] & 8) DG TODO TEST THIS CONDITION */) + { + const int top = _obstacles[index + _slabSize]; + const int bottom= _obstacles[index - _slabSize]; + const int up = _obstacles[index + _xRes]; + const int down = _obstacles[index - _xRes]; + const int left = _obstacles[index - 1]; + const int right = _obstacles[index + 1]; + + // unused + // const bool fullz = (top && bottom); + // const bool fully = (up && down); + //const bool fullx = (left && right); + + /* + _xVelocity[index] = + _yVelocity[index] = + _zVelocity[index] = 0.0f; + */ + _pressure[index] = 0.0f; + + // average pressure neighbors + float pcnt = 0.; + if (left && !right) { + _pressure[index] += _pressure[index + 1]; + pcnt += 1.0f; + } + if (!left && right) { + _pressure[index] += _pressure[index - 1]; + pcnt += 1.0f; + } + if (up && !down) { + _pressure[index] += _pressure[index - _xRes]; + pcnt += 1.0f; + } + if (!up && down) { + _pressure[index] += _pressure[index + _xRes]; + pcnt += 1.0f; + } + if (top && !bottom) { + _pressure[index] += _pressure[index - _slabSize]; + pcnt += 1.0f; + } + if (!top && bottom) { + _pressure[index] += _pressure[index + _slabSize]; + pcnt += 1.0f; + } + + if(pcnt > 0.000001f) + _pressure[index] /= pcnt; + + // TODO? set correct velocity bc's + // velocities are only set to zero right now + // this means it's not a full no-slip boundary condition + // but a "half-slip" - still looks ok right now } - - if(pcnt > 0.000001f) - _pressure[index] /= pcnt; - - // TODO? set correct velocity bc's - // velocities are only set to zero right now - // this means it's not a full no-slip boundary condition - // but a "half-slip" - still looks ok right now - } - //vIndex++; - } // x loop - //vIndex += 2; + //vIndex++; + } // x loop + //vIndex += 2; } // y loop //vIndex += 2 * _xRes; } // z loop @@ -1238,16 +1518,16 @@ void FLUID_3D::setObstaclePressure(float *_pressure, int zBegin, int zEnd) void FLUID_3D::setObstacleBoundaries(float *_pressure, int zBegin, int zEnd) { // cull degenerate obstacles , move to addObstacle? - + // r = b - Ax const size_t index_ = _slabSize + _xRes + 1; - + int bb=0; int bt=0; - + if (zBegin == 0) {bb = 1;} if (zEnd == _zRes) {bt = 1;} - + for (int z = zBegin + bb; z < zEnd - bt; z++) { size_t index = index_ +(z-1)*_slabSize; @@ -1264,7 +1544,7 @@ void FLUID_3D::setObstacleBoundaries(float *_pressure, int zBegin, int zEnd) const int down = _obstacles[index - _xRes]; const int left = _obstacles[index - 1]; const int right = _obstacles[index + 1]; - + int counter = 0; if (up) counter++; if (down) counter++; @@ -1272,7 +1552,7 @@ void FLUID_3D::setObstacleBoundaries(float *_pressure, int zBegin, int zEnd) if (right) counter++; if (top) counter++; if (bottom) counter++; - + if (counter < 3) _obstacles[index] = EMPTY; } @@ -1297,7 +1577,7 @@ void FLUID_3D::setObstacleBoundaries(float *_pressure, int zBegin, int zEnd) void FLUID_3D::addBuoyancy(float *heat, float *density, float gravity[3], int zBegin, int zEnd) { int index = zBegin*_slabSize; - + for (int z = zBegin; z < zEnd; z++) for (int y = 0; y < _yRes; y++) for (int x = 0; x < _xRes; x++, index++) @@ -1314,7 +1594,7 @@ void FLUID_3D::addBuoyancy(float *heat, float *density, float gravity[3], int zB // add vorticity to the force field ////////////////////////////////////////////////////////////////////// #define VORT_VEL(i, j) \ - ((_obstacles[obpos[(i)]] & 8) ? ((abs(objvelocity[(j)][obpos[(i)]]) > FLT_EPSILON) ? objvelocity[(j)][obpos[(i)]] : velocity[(j)][index]) : velocity[(j)][obpos[(i)]]) +((_obstacles[obpos[(i)]] & 8) ? ((abs(objvelocity[(j)][obpos[(i)]]) > FLT_EPSILON) ? objvelocity[(j)][obpos[(i)]] : velocity[(j)][index]) : velocity[(j)][obpos[(i)]]) void FLUID_3D::addVorticity(int zBegin, int zEnd) { @@ -1322,54 +1602,54 @@ void FLUID_3D::addVorticity(int zBegin, int zEnd) float flame_vorticity = (*_flame_vorticity)/_constantScaling; //int x,y,z,index; if(_vorticityEps+flame_vorticity<=0.0f) return; - + int _blockSize=zEnd-zBegin; int _blockTotalCells = _slabSize * (_blockSize+2); - + float *_xVorticity, *_yVorticity, *_zVorticity, *_vorticity; - + int bb=0; int bt=0; int bb1=-1; int bt1=-1; - + if (zBegin == 0) {bb1 = 1; bb = 1; _blockTotalCells-=_blockSize;} if (zEnd == _zRes) {bt1 = 1;bt = 1; _blockTotalCells-=_blockSize;} - + _xVorticity = new float[_blockTotalCells]; _yVorticity = new float[_blockTotalCells]; _zVorticity = new float[_blockTotalCells]; _vorticity = new float[_blockTotalCells]; - + memset(_xVorticity, 0, sizeof(float)*_blockTotalCells); memset(_yVorticity, 0, sizeof(float)*_blockTotalCells); memset(_zVorticity, 0, sizeof(float)*_blockTotalCells); memset(_vorticity, 0, sizeof(float)*_blockTotalCells); - + //const size_t indexsetupV=_slabSize; const size_t index_ = _slabSize + _xRes + 1; - + // calculate vorticity float gridSize = 0.5f / _dx; //index = _slabSize + _xRes + 1; - + float *velocity[3]; float *objvelocity[3]; - + velocity[0] = _xVelocity; velocity[1] = _yVelocity; velocity[2] = _zVelocity; - + objvelocity[0] = _xVelocityOb; objvelocity[1] = _yVelocityOb; objvelocity[2] = _zVelocityOb; - + size_t vIndex=_xRes + 1; for (int z = zBegin + bb1; z < (zEnd - bt1); z++) { size_t index = index_ +(z-1)*_slabSize; vIndex = index-(zBegin-1+bb)*_slabSize; - + for (int y = 1; y < _yRes - 1; y++, index += 2) { for (int x = 1; x < _xRes - 1; x++, index++) @@ -1377,43 +1657,43 @@ void FLUID_3D::addVorticity(int zBegin, int zEnd) if (!_obstacles[index]) { int obpos[6]; - + obpos[0] = (_obstacles[index + _xRes] == 1) ? index : index + _xRes; // up obpos[1] = (_obstacles[index - _xRes] == 1) ? index : index - _xRes; // down float dy = (obpos[0] == index || obpos[1] == index) ? 1.0f / _dx : gridSize; - + obpos[2] = (_obstacles[index + _slabSize] == 1) ? index : index + _slabSize; // out obpos[3] = (_obstacles[index - _slabSize] == 1) ? index : index - _slabSize; // in float dz = (obpos[2] == index || obpos[3] == index) ? 1.0f / _dx : gridSize; - + obpos[4] = (_obstacles[index + 1] == 1) ? index : index + 1; // right obpos[5] = (_obstacles[index - 1] == 1) ? index : index - 1; // left float dx = (obpos[4] == index || obpos[5] == index) ? 1.0f / _dx : gridSize; - + float xV[2], yV[2], zV[2]; - + zV[1] = VORT_VEL(0, 2); zV[0] = VORT_VEL(1, 2); yV[1] = VORT_VEL(2, 1); yV[0] = VORT_VEL(3, 1); _xVorticity[vIndex] = (zV[1] - zV[0]) * dy + (-yV[1] + yV[0]) * dz; - + xV[1] = VORT_VEL(2, 0); xV[0] = VORT_VEL(3, 0); zV[1] = VORT_VEL(4, 2); zV[0] = VORT_VEL(5, 2); _yVorticity[vIndex] = (xV[1] - xV[0]) * dz + (-zV[1] + zV[0]) * dx; - + yV[1] = VORT_VEL(4, 1); yV[0] = VORT_VEL(5, 1); xV[1] = VORT_VEL(0, 0); xV[0] = VORT_VEL(1, 0); _zVorticity[vIndex] = (yV[1] - yV[0]) * dx + (-xV[1] + xV[0])* dy; - + _vorticity[vIndex] = sqrtf(_xVorticity[vIndex] * _xVorticity[vIndex] + - _yVorticity[vIndex] * _yVorticity[vIndex] + - _zVorticity[vIndex] * _zVorticity[vIndex]); - + _yVorticity[vIndex] * _yVorticity[vIndex] + + _zVorticity[vIndex] * _zVorticity[vIndex]); + } vIndex++; } @@ -1421,44 +1701,44 @@ void FLUID_3D::addVorticity(int zBegin, int zEnd) } //vIndex+=2*_xRes; } - + // calculate normalized vorticity vectors float eps = _vorticityEps; //index = _slabSize + _xRes + 1; vIndex=_slabSize + _xRes + 1; - + for (int z = zBegin + bb; z < (zEnd - bt); z++) { size_t index = index_ +(z-1)*_slabSize; vIndex = index-(zBegin-1+bb)*_slabSize; - + for (int y = 1; y < _yRes - 1; y++, index += 2) { for (int x = 1; x < _xRes - 1; x++, index++) { // - + if (!_obstacles[index]) { float N[3]; - + int up = (_obstacles[index + _xRes] == 1) ? vIndex : vIndex + _xRes; int down = (_obstacles[index - _xRes] == 1) ? vIndex : vIndex - _xRes; float dy = (up == vIndex || down == vIndex) ? 1.0f / _dx : gridSize; - + int out = (_obstacles[index + _slabSize] == 1) ? vIndex : vIndex + _slabSize; int in = (_obstacles[index - _slabSize] == 1) ? vIndex : vIndex - _slabSize; float dz = (out == vIndex || in == vIndex) ? 1.0f / _dx : gridSize; - + int right = (_obstacles[index + 1] == 1) ? vIndex : vIndex + 1; int left = (_obstacles[index - 1] == 1) ? vIndex : vIndex - 1; float dx = (right == vIndex || left == vIndex) ? 1.0f / _dx : gridSize; - + N[0] = (_vorticity[right] - _vorticity[left]) * dx; N[1] = (_vorticity[up] - _vorticity[down]) * dy; N[2] = (_vorticity[out] - _vorticity[in]) * dz; - + float magnitude = sqrtf(N[0] * N[0] + N[1] * N[1] + N[2] * N[2]); if (magnitude > FLT_EPSILON) { @@ -1467,19 +1747,19 @@ void FLUID_3D::addVorticity(int zBegin, int zEnd) N[0] *= magnitude; N[1] *= magnitude; N[2] *= magnitude; - + _xForce[index] += (N[1] * _zVorticity[vIndex] - N[2] * _yVorticity[vIndex]) * _dx * (eps + flame_vort); _yForce[index] += (N[2] * _xVorticity[vIndex] - N[0] * _zVorticity[vIndex]) * _dx * (eps + flame_vort); _zForce[index] += (N[0] * _yVorticity[vIndex] - N[1] * _xVorticity[vIndex]) * _dx * (eps + flame_vort); } - } // if - vIndex++; - } // x loop - vIndex+=2; - } // y loop - //vIndex+=2*_xRes; - } // z loop - + } // if + vIndex++; + } // x loop + vIndex+=2; + } // y loop + //vIndex+=2*_xRes; + } // z loop + if (_xVorticity) delete[] _xVorticity; if (_yVorticity) delete[] _yVorticity; if (_zVorticity) delete[] _zVorticity; @@ -1490,7 +1770,7 @@ void FLUID_3D::addVorticity(int zBegin, int zEnd) void FLUID_3D::advectMacCormackBegin(int zBegin, int zEnd) { Vec3Int res = Vec3Int(_xRes,_yRes,_zRes); - + setZeroX(_xVelocityOld, res, zBegin, zEnd); setZeroY(_yVelocityOld, res, zBegin, zEnd); setZeroZ(_zVelocityOld, res, zBegin, zEnd); @@ -1502,16 +1782,16 @@ void FLUID_3D::advectMacCormackBegin(int zBegin, int zEnd) void FLUID_3D::advectMacCormackEnd1(int zBegin, int zEnd) { Vec3Int res = Vec3Int(_xRes,_yRes,_zRes); - + const float dt0 = _dt / _dx; - + int begin=zBegin * _slabSize; int end=begin + (zEnd - zBegin) * _slabSize; for (int x = begin; x < end; x++) _xForce[x] = 0.0; - + // advectFieldMacCormack1(dt, xVelocity, yVelocity, zVelocity, oldField, newField, res) - + advectFieldMacCormack1(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _densityOld, _densityTemp, res, zBegin, zEnd); if (_heat) { advectFieldMacCormack1(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _heatOld, _heatTemp, res, zBegin, zEnd); @@ -1528,7 +1808,7 @@ void FLUID_3D::advectMacCormackEnd1(int zBegin, int zEnd) advectFieldMacCormack1(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _xVelocityOld, _xVelocity, res, zBegin, zEnd); advectFieldMacCormack1(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _yVelocityOld, _yVelocity, res, zBegin, zEnd); advectFieldMacCormack1(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _zVelocityOld, _zVelocity, res, zBegin, zEnd); - + // Have to wait untill all the threads are done -> so continuing in step 3 } @@ -1539,12 +1819,12 @@ void FLUID_3D::advectMacCormackEnd2(int zBegin, int zEnd) { const float dt0 = _dt / _dx; Vec3Int res = Vec3Int(_xRes,_yRes,_zRes); - + // use force array as temp array float* t1 = _xForce; - + // advectFieldMacCormack2(dt, xVelocity, yVelocity, zVelocity, oldField, newField, tempfield, temp, res, obstacles) - + /* finish advection */ advectFieldMacCormack2(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _densityOld, _density, _densityTemp, t1, res, _obstacles, zBegin, zEnd); if (_heat) { @@ -1562,17 +1842,17 @@ void FLUID_3D::advectMacCormackEnd2(int zBegin, int zEnd) advectFieldMacCormack2(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _xVelocityOld, _xVelocityTemp, _xVelocity, t1, res, _obstacles, zBegin, zEnd); advectFieldMacCormack2(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _yVelocityOld, _yVelocityTemp, _yVelocity, t1, res, _obstacles, zBegin, zEnd); advectFieldMacCormack2(dt0, _xVelocityOld, _yVelocityOld, _zVelocityOld, _zVelocityOld, _zVelocityTemp, _zVelocity, t1, res, _obstacles, zBegin, zEnd); - + /* set boundary conditions for velocity */ if(!_domainBcLeft) copyBorderX(_xVelocityTemp, res, zBegin, zEnd); else setZeroX(_xVelocityTemp, res, zBegin, zEnd); - + if(!_domainBcFront) copyBorderY(_yVelocityTemp, res, zBegin, zEnd); else setZeroY(_yVelocityTemp, res, zBegin, zEnd); - + if(!_domainBcTop) copyBorderZ(_zVelocityTemp, res, zBegin, zEnd); else setZeroZ(_zVelocityTemp, res, zBegin, zEnd); - + /* clear data boundaries */ setZeroBorder(_density, res, zBegin, zEnd); if (_fuel) { @@ -1594,7 +1874,7 @@ void FLUID_3D::processBurn(float *fuel, float *smoke, float *react, float *heat, float flame_smoke = *_flame_smoke; float ignition_point = *_ignition_temp; float temp_max = *_max_temp; - + for (int index = 0; index < total_cells; index++) { float orig_fuel = fuel[index]; @@ -1613,7 +1893,7 @@ void FLUID_3D::processBurn(float *fuel, float *smoke, float *react, float *heat, else { react[index] = 0.0f; } - + /* emit smoke based on fuel burn rate and "flame_smoke" factor */ smoke_emit = (orig_fuel < 1.0f) ? (1.0f - orig_fuel)*0.5f : 0.0f; smoke_emit = (smoke_emit + 0.5f) * (orig_fuel-fuel[index]) * 0.1f * flame_smoke; diff --git a/intern/smoke/intern/FLUID_3D.h b/intern/smoke/intern/FLUID_3D.h index cd2147b2bee..b0fab50b843 100644 --- a/intern/smoke/intern/FLUID_3D.h +++ b/intern/smoke/intern/FLUID_3D.h @@ -46,7 +46,7 @@ struct WTURBULENCE; struct FLUID_3D { public: - FLUID_3D(int *res, float dx, float dtdef, int init_heat, int init_fire, int init_colors); + FLUID_3D(int *res, float dx, float dtdef, int init_heat, int init_fire, int init_colors, struct SmokeModifierData *smd); FLUID_3D() {}; virtual ~FLUID_3D(); @@ -112,6 +112,10 @@ struct FLUID_3D float* _zForce; unsigned char* _obstacles; /* only used (useful) for static obstacles like domain boundaries */ unsigned char* _obstaclesAnim; + float* _manta_inflow; + bool using_heat; + int manta_resoution; + int _yLocation;/*at which Y-coordinate store the information*/ // Required for proper threading: float* _xVelocityTemp; @@ -139,8 +143,9 @@ struct FLUID_3D float *_color_b; float *_color_bOld; float *_color_bTemp; + bool using_colors; - + int *_manta_flags; // CG fields int _iterations; diff --git a/intern/smoke/intern/MANTA.cpp b/intern/smoke/intern/MANTA.cpp new file mode 100644 index 00000000000..6772fb44759 --- /dev/null +++ b/intern/smoke/intern/MANTA.cpp @@ -0,0 +1,666 @@ +#include "MANTA.h" +#include "WTURBULENCE.h" +#include "scenarios/smoke.h" + +extern "C" bool manta_check_grid_size(struct FLUID_3D *fluid, int dimX, int dimY, int dimZ) +{ + /*Y and Z axes are swapped in manta and blender*/ + if (!(dimX == fluid->xRes() && dimY == fluid->yRes() && dimZ == fluid->zRes())) { + for (int cnt(0); cnt < fluid->_totalCells; cnt++) + fluid->_density[cnt] = 0.0f; + return false; + } + return true; +} + +extern "C" bool manta_check_wavelets_size(struct WTURBULENCE *wt, int dimX, int dimY, int dimZ) +{ + /*Y and Z axes are swapped in manta and blender*/ + if (!(dimX == wt->_xResBig && dimY == wt->_yResBig && dimZ == wt->_zResBig)) { + for (int cnt(0); cnt < wt->_totalCellsBig; cnt++) + wt->_densityBig[cnt] = 0.0f; + return false; + } + return true; +} + +void read_rotated_grid(gzFile gzf, float *data, int size_x, int size_y, int size_z) +{ + assert(size_x > 1 && size_y > 1 && size_z > 1); + float* temp_data = (float*)malloc(sizeof(float) * size_x * size_y * size_z); +// data = (float*)malloc(sizeof(float) * size_x * size_y * size_z); + gzread(gzf, temp_data, sizeof(float)* size_x * size_y * size_z); + for (int cnt_x(0); cnt_x < size_x; ++cnt_x) +{ + for (int cnt_y(0); cnt_y < size_y; ++cnt_y) + { + for (int cnt_z(0); cnt_z < size_z; ++cnt_z) + { + data[cnt_x + size_x * cnt_y + size_x*size_y * cnt_z] = temp_data[cnt_x + size_x * cnt_y + size_x*size_y * cnt_z]; + } + } + } +} + +static void wavelets_add_lowres_density(SmokeDomainSettings *sds) +{ + assert(sds != NULL); + for (int cnt_x(0); cnt_x < sds->wt->_xResBig; ++cnt_x) + { + for (int cnt_y(0); cnt_y < sds->wt->_yResBig; ++cnt_y) + { + for (int cnt_z(0); cnt_z < sds->wt->_zResBig; ++cnt_z) + { + //scale down to domain res + float x_sc = 1. * sds->base_res[0] * cnt_x / sds->wt->_xResBig; + float y_sc = 1. * sds->base_res[1] * cnt_y / sds->wt->_yResBig; + float z_sc = 1. * sds->base_res[2] * cnt_z / sds->wt->_zResBig; + //finding cells to interpolate from + int start_x = int(x_sc / 1); + int start_y = int(y_sc / 1); + int start_z = int(z_sc / 1); + int end_x = ((x_sc - start_x > 0.001) && (start_x + 1 < sds->base_res[0]))? start_x + 1: start_x; + int end_y = ((y_sc - start_y > 0.001) && (start_y + 1 < sds->base_res[1]))? start_y + 1: start_y; + int end_z = ((z_sc - start_z > 0.001) && (start_z + 1 < sds->base_res[2]))? start_z + 1: start_z; + //interpolation + float add_value = 0; + int cnt=0; + for(int x(start_x); x <= end_x; ++x) + { + for(int y(start_y); y <= end_y; ++y) + { + for(int z(start_z); z <= end_z; ++z) + { + cnt++; + add_value += sds->fluid->_density[x + y*sds->base_res[0] + z * sds->base_res[0]*sds->base_res[1]]; + } + } + } + add_value /= float(cnt); + sds->wt->_densityBig[cnt_x + cnt_y *sds->wt->_xResBig + cnt_z*sds->wt->_xResBig*sds->wt->_yResBig] += add_value; + } + } + } +} + +//PR need SMD data here for wavelets +extern "C" int read_mantaflow_sim(struct SmokeDomainSettings *sds, char *name, bool reading_wavelets) +{ + /*! l /*! legacy headers for reading old files */ + typedef struct { + int dimX, dimY, dimZ; + int frames, elements, elementType, bytesPerElement, bytesPerFrame; + } UniLegacyHeader; + + typedef struct { + int dimX, dimY, dimZ; + int gridType, elementType, bytesPerElement; + } UniLegacyHeader2; + + /* uni file header - currently used */ + typedef struct { + int dimX, dimY, dimZ; + int gridType, elementType, bytesPerElement; + char info[256]; /* mantaflow build information */ + unsigned long long timestamp; /* creation time */ + } UniHeader; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name, "rb"); +// if (!gzf) { +// if(reading_wavelets){ +// for (int cnt(0); cnt < sds->wt->_totalCellsBig; cnt++) +// sds->wt->_densityBig[cnt] = 0.0f; +// } +// else{ +// for (int cnt(0); cnt < sds->fluid->_totalCells; cnt++) +// sds->fluid->_density[cnt] = 0.0f; +// } +// return 0; +// } + + char ID[5] = {0,0,0,0,0}; + gzread(gzf, ID, 4); + /* legacy file format */ + if (!strcmp(ID, "DDF2")) { + UniLegacyHeader head; + gzread(gzf, &head, sizeof(UniLegacyHeader)); + int numEl = head.dimX*head.dimY*head.dimZ; + gzseek(gzf, numEl, SEEK_CUR); + /* actual grid read */ + if ( ! reading_wavelets){ +// if (!manta_check_grid_size(sds->fluid, head.dimX, head.dimY, head.dimZ)) return 0; + gzread(gzf, sds->fluid->_density, sizeof(float)*numEl); + } + else { + if (!manta_check_wavelets_size(sds->wt, head.dimX, head.dimY, head.dimZ)) return 0; + gzread(gzf, sds->wt->_densityBig, sizeof(float)*numEl); + } + } + /* legacy file format 2 */ + else if (!strcmp(ID, "MNT1")) { + UniLegacyHeader2 head; + gzread(gzf, &head, sizeof(UniLegacyHeader2)); + /* actual grid read*/ + if ( ! reading_wavelets){ +// if (!manta_check_grid_size(sds->fluid, head.dimX, head.dimY, head.dimZ)) return 0; + gzread(gzf, sds->fluid->_density, sizeof(float)*head.dimX*head.dimY*head.dimZ); + } + else{ + if (!manta_check_wavelets_size(sds->wt, head.dimX, head.dimY, head.dimZ)) return 0; + gzread(gzf, sds->wt->_densityBig, sizeof(float)*head.dimX*head.dimY*head.dimZ); + } + } + /* current file format*/ + else if (!strcmp(ID, "MNT2")) { + UniHeader head; + gzread(gzf, &head, sizeof(UniHeader)); + /* actual grid read */ + if ( ! reading_wavelets){ +// if (!manta_check_grid_size(sds->fluid, head.dimX, head.dimY, head.dimZ)) return 0; + /*Y and Z axes are swapped in manta and blender*/ + gzread(gzf,sds->fluid->_density, sizeof(float)*head.dimX*head.dimY*head.dimZ); + + } + else{ + if (!manta_check_wavelets_size(sds->wt, head.dimX, head.dimY, head.dimZ)) return 0; + /*Y and Z axes are swapped in manta and blender*/ + gzread(gzf,sds->wt->_densityBig, sizeof(float)*head.dimX*head.dimY*head.dimZ); + gzread(gzf,sds->wt->_densityBigOld, sizeof(float)*head.dimX*head.dimY*head.dimZ); +// wavelets_add_lowres_density(sds); + } + } + gzclose(gzf); + return 1; +# endif /*zlib*/ + return 0; +} + + +void Manta_API::indent_ss(stringstream& ss, int indent) +{ + /*two-spaces indent*/ + if (indent < 0) return; + std::string indentation = ""; + for (size_t cnt(0); cnt < indent; ++cnt) { + indentation += " "; + } + ss << indentation; +} + +void Manta_API::manta_gen_noise(stringstream& ss, char* solver, int indent, char *noise, int seed, bool load, bool clamp, float clampNeg, float clampPos, float valScale, float valOffset, float timeAnim) +{ + if (ss == NULL)/*should never be here*/ + { + return; + } + indent_ss(ss, indent); + ss << noise << " = "<<solver<<".create(NoiseField, fixedSeed=" << seed << ", loadFromFile="<< (load?"True":"False") <<") \n"; + ss << noise << ".posScale = vec3(20) \n"; + ss << noise << ".clamp = " << ((clamp)?"True":"False") << " \n"; + ss << noise << ".clampNeg = " << clampNeg << " \n"; + ss << noise << ".clampPos = " << clampPos << " \n"; + ss << noise << ".valScale = " << valScale << " \n"; + ss << noise << ".valOffset = " << valOffset << " \n"; + ss << noise << ".timeAnim = " << timeAnim << " \n"; +} + +void Manta_API::manta_solve_pressure(stringstream& ss, char *flags, char *vel, char *pressure, bool useResNorms, int openBound, int solver_res,float cgMaxIterFac, float cgAccuracy) +{ + /*open:0 ; vertical : 1; closed:2*/ + ss << " solvePressure(flags=" << flags << ", vel=" << vel << ", pressure=" << pressure << ", useResNorm=" << (useResNorms?"True":"False") << ", openBound='"; + + if(openBound == 1) /*vertical*/ + { + ss << "yY'"; + } + else if (openBound == 0) /*open*/ + { + if(solver_res == 2) + ss << "xXyY"; + else + ss << "xXyYzZ"; + } + ss << "'"; /*empty for closed bounds*/ + + ss << ", cgMaxIterFac=" << cgMaxIterFac << ", cgAccuracy=" << cgAccuracy << ") \n"; +} + +void Manta_API::manta_advect_SemiLagr(stringstream& ss, int indent, char *flags, char *vel, char *grid, int order) +{ + if((order <=1) || (flags == NULL) || (vel == NULL) || (grid == NULL)){return;} + indent_ss(ss, indent); + ss << "advectSemiLagrange(flags=" << flags << ", vel=" << vel \ + << ", grid=" << grid << ", order=" << order << ") \n"; +} + +/*create solver, handle 2D case*/ +void Manta_API::manta_create_solver(stringstream& ss, char *name, char *nick, char *grid_size_name, int x_res, int y_res, int z_res, int dim) +{ + if ((dim != 2) && (dim != 3)) + { return; } + if (dim == 2) + { y_res = 1; } + ss << grid_size_name << " = vec3(" << x_res << ", " << y_res << ", " << z_res << ")" << " \n"; + ss << name << " = Solver(name = '" << nick << "', gridSize = " << grid_size_name << ", dim = " << dim << ") \n"; +} + +inline bool file_exists (const std::string& name) { + return ( access( name.c_str(), F_OK ) != -1 ); +} + +/*blender transforms obj coords to [-1,1]. This method transforms them back*/ +void Manta_API::add_mesh_transform_method(stringstream& ss) +{ + ss << "def transform_back(obj, gs):\n" << + " obj.scale(gs/2)\n" << + " obj.offset(gs/2)\n\n"; +} + +void Manta_API::manta_cache_path(char *filepath) +{ + char *name="manta"; + BLI_make_file_string("/", filepath, BLI_temp_dir_session(), name); +} + +void Manta_API::stop_manta_sim() +{ + pthread_cancel(manta_thread); +} + +string Manta_API::gridNameFromType(const string &type) +{ + if (type == "float") + { + return "RealGrid"; + } + else if (type == "Vec3") + { + return "MACGrid"; + } + else + { + cout<<"ERROR: can not create grid from type: "<< type << endl; + return ""; + } +} + +void Manta_API::addGrid(void * data, string name, string type, int x, int y, int z, bool is2D = false) +{ + if (data == NULL || name == "" || gridNameFromType(type) == "") return; + cout << "Adding Grid:" << name<<endl; + std::ostringstream stringStream; + stringStream << "temp_" << name; + std::string grid_name = stringStream.str(); + stringStream.str(""); + stringStream << grid_name << " = s.create(" << gridNameFromType(type) << ")"; + const std::string command_1 = stringStream.str(); + stringStream.str(""); + if (is2D){ + /*for 2D case, Y and Z axes are switched, Y axis is '1' for Mantaflow*/ + stringStream << grid_name << ".readGridFromMemory(\'"<< data << "\', " << x << "," << z << "," << 1 << ")"; + } + else{ + stringStream << grid_name << ".readGridFromMemory(\'"<< data << "\', " << x << "," << y << "," << z << ")"; + } + const std::string command_2 = stringStream.str(); + const std::string command_3 = name + ".add(" + grid_name + ")"; + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString(command_1.c_str()); + PyRun_SimpleString(command_2.c_str()); + PyRun_SimpleString(command_3.c_str()); + PyGILState_Release(gilstate); +} + +void Manta_API::addAdaptiveGrid(void * data, string gridName, string solverName, string type, int minX, int minY, int minZ, int maxX, int maxY, int maxZ, bool is2D = false) +{ + if (data == NULL || gridName == "" || gridNameFromType(type) == "") return; + { + cout << "NULL values passed to grid addAdaptiveGrid for grid " << gridName <<endl; + return; + } + std::ostringstream stringStream; + stringStream << "temp_" <<gridName; + std::string temp_grid_name = stringStream.str(); + stringStream.str(""); + stringStream << temp_grid_name << " = "<< solverName << ".create(" << gridNameFromType(type) << ")"; + const std::string command_1 = stringStream.str(); + stringStream.str(""); + + if (is2D){ + stringStream << temp_grid_name << ".readAdaptiveGridFromMemory(\'"<< data << "\', vec3(" << minX << "," << minZ << "," << 1 << + "), vec3(" << maxX << "," << maxZ << "," << 1 << ") )"; + } + else{ + stringStream << temp_grid_name << ".readAdaptiveGridFromMemory(\'"<< data << "\', vec3(" << minX << "," << minY << "," << minZ << + "), vec3(" << maxX << "," << maxY << "," << maxZ << ") )"; + } + const std::string command_2 = stringStream.str(); + const std::string command_3 = gridName + ".add(" + temp_grid_name + ")"; + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString("print('Reading Adaptive grid from memory')"); + PyRun_SimpleString("print (s)"); + PyRun_SimpleString(command_1.c_str()); + PyRun_SimpleString(command_2.c_str()); + PyRun_SimpleString(command_3.c_str()); + PyGILState_Release(gilstate); +} + +void Manta_API::export_obstacles(float *data, int x, int y, int z, bool is2D = false) +{ + if (data == NULL){ + cout << "NULL passed to grid export_obstacles " <<endl; return; + } + std::ostringstream stringStream; + std::string grid_name = "obs_sdf"; + stringStream.str(""); + stringStream << grid_name << " = s.create(RealGrid)"; + const std::string command_1 = stringStream.str(); + stringStream.str(""); + cout<<"Exporting obstacles"<<endl; + if (is2D){ + stringStream << grid_name << ".readGridFromMemory(\'"<< data << "\', " << x << "," << z << "," << 1 << ")"; + } + else{ + stringStream << grid_name << ".readGridFromMemory(\'"<< data << "\', " << x << "," << y << "," << z << ")"; + } + const std::string command_2 = stringStream.str(); + const std::string command_3 = grid_name + ".applyToGrid(grid = flags, value = FlagObstacle)"; + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString(command_1.c_str()); + PyRun_SimpleString(command_2.c_str()); + PyRun_SimpleString(command_3.c_str()); + PyGILState_Release(gilstate); +} + +void Manta_API::run_manta_sim_highRes(WTURBULENCE *wt) +{ + if (wt == NULL){ + cout << "ERROR: cannot run wt step, wt object is NULL " <<endl; return; + } + PyGILState_STATE gilstate = PyGILState_Ensure(); + int sim_frame = 1; +// manta_write_effectors(fluid); + std::string frame_str = static_cast<ostringstream*>( &(ostringstream() << sim_frame) )->str(); + std::string py_string_0 = string("sim_step_high(").append(frame_str); + std::string py_string_1 = py_string_0.append(")\0"); + cout << "Debug C++: densityPointer:" << Manta_API::getGridPointer("density", "s")<<endl; + PyRun_SimpleString("print ('pyhton density pointer:' + density.getDataPointer())"); + PyRun_SimpleString(py_string_1.c_str()); + cout<< "done"<<manta_sim_running<<endl; + PyGILState_Release(gilstate); + updateHighResPointers(wt,false); +} + +void Manta_API::generate_manta_sim_file_highRes(SmokeModifierData *smd) +{ + string smoke_script = smoke_setup_high + smoke_step_high; + std::string final_script = parseScript(smoke_script, smd); + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString(final_script.c_str()); + PyGILState_Release(gilstate); +} + +std::string Manta_API::getRealValue( const std::string& varName, SmokeModifierData *smd) +{ + ostringstream ss; + bool is2D = smd->domain->fluid->manta_resoution == 2; + if (varName == "UVS_CNT") + ss << smd->domain->manta_uvs_num ; + else if (varName == "UPRES") + ss << smd->domain->amplify+1; + else if (varName == "WLT_STR") + ss << smd->domain->strength ; + else if (varName == "RES") + ss << smd->domain->maxres; + else if (varName == "RESX") + ss << smd->domain->fluid->_xRes; + + else if (varName == "RESY") + if (is2D){ ss << smd->domain->fluid->_zRes;} + else{ ss << smd->domain->fluid->_yRes;} + + else if (varName == "RESZ") + if (is2D){ ss << 1;} + else{ ss << smd->domain->fluid->_zRes;} + + else if (varName == "SOLVER_DIM") + ss << smd->domain->manta_solver_res; + else if (varName == "NOISE_CN") + ss << smd->domain->noise_clamp_neg; + else if (varName == "NOISE_CP") + ss << smd->domain->noise_clamp_pos; + else if (varName == "NOISE_VALSCALE") + ss << smd->domain->noise_val_scale; + else if (varName == "NOISE_VALOFFSET") + ss << smd->domain->noise_val_offset; + else if (varName == "NOISE_TIMEANIM") + ss << smd->domain->noise_time_anim; + else if (varName == "HRESX") + ss << smd->domain->wt->getResBig()[0]; + else if (varName == "HRESY") + if (is2D){ ss << smd->domain->wt->getResBig()[2];} + else{ ss << smd->domain->wt->getResBig()[1];} + + else if (varName == "HRESZ") + if (is2D){ ss << 1;} + else{ ss << smd->domain->wt->getResBig()[2];} + + else if (varName == "TIMESTEP") + ss << smd->domain->time_scale * 0.1f; + else if (varName == "XL_TIMESTEP") + ss << smd->domain->time_scale * 0.1f; + else if (varName == "USE_WAVELETS") + ss << (smd->domain->flags & MOD_SMOKE_HIGHRES)?"True":"False"; + else if (varName == "BUYO_X") + ss << 0.; + else if (varName == "BUYO_Y") + ss << 0.; + else if (varName == "BUYO_Z") + ss << (-smd->domain->beta); + else if (varName == "ALPHA") + ss << (-smd->domain->alpha); + else if (varName == "BETA") + ss << (-smd->domain->beta); + else if (varName == "ADVECT_ORDER") + ss << 2; + else if (varName == "MANTA_EXPORT_PATH"){ + char parent_dir[1024]; + BLI_split_dir_part(smd->domain->_manta_filepath, parent_dir, sizeof(parent_dir)); + ss << parent_dir; + } + else if (varName == "VORTICITY"){ + cout << "Vorticity :" << smd->domain->vorticity / smd->domain->fluid->_constantScaling << endl; + ss << smd->domain->vorticity / smd->domain->fluid->_constantScaling; + }else if (varName == "BOUNDCONDITIONS"){ + if(smd->domain->border_collisions == SM_BORDER_OPEN) ss << "xXyY"; + else if (smd->domain->border_collisions == SM_BORDER_VERTICAL) ss << "xXyY"; + else if (smd->domain->border_collisions == SM_BORDER_CLOSED) ss << "xXyY"; + + if (smd->domain->manta_solver_res == 3){ + if(smd->domain->border_collisions == SM_BORDER_OPEN) ss << "z"; + else if (smd->domain->border_collisions == SM_BORDER_VERTICAL) ss << "z"; + else if (smd->domain->border_collisions == SM_BORDER_CLOSED) ss << "zZ"; + } + } + else if (varName == "GRAVITY") + ss << "vec3(0,0,-0.981)"; + else if (varName == "ABS_FLOW") + ss << (smd->flow->flags & MOD_SMOKE_FLOW_ABSOLUTE)?"True":"False"; + else if (varName == "DENSITY_MEM") + ss << smd->domain->fluid->_density; + else if (varName == "DENSITY_SIZE") + ss << sizeof(float) * smd->domain->total_cells; + else if (varName == "XL_DENSITY_MEM") + ss << smd->domain->wt->_densityBig; + else if (varName == "XL_DENSITY_SIZE") + ss << sizeof(float) * smd->domain->wt->_xResBig * smd->domain->wt->_yResBig * smd->domain->wt->_zResBig; + else + cout<< "ERROR: Unknown option:"<< varName <<endl; + return ss.str(); +} + +std::string Manta_API::parseLine(const string& line, SmokeModifierData *smd) +{ + 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), smd); + } + currPos ++; + } + res += line.substr(end_del+1, line.size()- end_del); + return res; +} + +std::string Manta_API::parseScript(const string & setup_string, SmokeModifierData *smd) +{ + std::istringstream f(setup_string); + ostringstream res; + string line=""; + while(getline(f,line)){ + res << parseLine(line,smd) << "\n"; + } + return res.str(); +} + +void Manta_API::manta_export_grids(SmokeModifierData *smd){ + std::string smoke_script; + if (smd->domain->flags & MOD_SMOKE_MANTA_USE_LIQUID) + smoke_script = smoke_setup_low + liquid_step_low; + else + smoke_script = smoke_setup_low + smoke_step_low; + std::string final_script = Manta_API::parseScript(smoke_script, smd) + standalone; + ofstream myfile; + myfile.open (smd->domain->_manta_filepath); + myfile << final_script; + myfile.close(); + + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyRun_SimpleString(Manta_API::parseScript(smoke_export_low,smd).c_str()); + PyGILState_Release(gilstate); +} + +string Manta_API::getGridPointer(std::string gridName, std::string solverName) +{ + if ((gridName == "") && (solverName == "")){ + return ""; + } + + PyGILState_STATE gilstate = PyGILState_Ensure(); + PyObject *main = PyImport_AddModule("__main__"); + if (main == NULL){cout << "null" << 1 << endl;return "";} + PyObject *globals = PyModule_GetDict(main); + if (globals == NULL){cout << "null" << 12 << endl;return "";} + PyObject *grid_object = PyDict_GetItemString(globals, gridName.c_str()); + if (grid_object == NULL){cout << "null" << 13 << endl;return "";} + PyObject* func = PyObject_GetAttrString(grid_object,(char*)"getDataPointer"); + if (func == NULL){cout << "null" << 14 << endl;return "";} + PyObject* retured_value = PyObject_CallObject(func, NULL); + PyObject* encoded = PyUnicode_AsUTF8String(retured_value); + if (retured_value == NULL){cout << "null" << 15 << endl;return "";} + std::string res = strdup(PyBytes_AsString(encoded)); + cout << "Pointer on "<< gridName << " " << res << endl; + PyGILState_Release(gilstate); + return res; +} + +// init direct access functions from blender +void Manta_API::initBlenderRNA(float *alpha, float *beta, float *dt_factor, float *vorticity, int *borderCollision, float *burning_rate, + float *flame_smoke, float *flame_smoke_color, float *flame_vorticity, float *flame_ignition_temp, float *flame_max_temp) +{ + _alpha = alpha; + _beta = beta; + _dtFactor = dt_factor; + _vorticityRNA = vorticity; + _borderColli = borderCollision; + _burning_rate = burning_rate; + _flame_smoke = flame_smoke; + _flame_smoke_color = flame_smoke_color; + _flame_vorticity = flame_vorticity; + _ignition_temp = flame_ignition_temp; + _max_temp = flame_max_temp; +} + +void * Manta_API::pointerFromString(const std::string& s){ + stringstream ss(s); + void *gridPointer = NULL; + ss >> gridPointer; + return gridPointer; +} + + +void Manta_API::updatePointers(FLUID_3D *fluid, bool updateColor) +{ + //blender_to_manta: whether we copy data from blender density/velocity field to mantaflow or the other way around + /*in 2D case, we want to copy in the Z-axis field that is in the middle of X and Y axes */ + //x + y * max_x + z * max_x*max_y +// int position_to_copy_from(0 + (fluid->xRes()/2) * fluid->xRes() + (fluid->zRes()/2) * fluid->xRes()*fluid->yRes()); +// float *whereToCopy = &fluid->_density[position_to_copy_from]; + if (fluid->manta_resoution == 2) + { + float* manta_fluid_density = (float* )pointerFromString(getGridPointer("density", "s")); + int* manta_fluid_flags = (int* )pointerFromString(getGridPointer("flags", "s")); + if (fluid->_density != NULL){ + for (int cnt(0); cnt < fluid->xRes() * fluid->yRes() * fluid->zRes(); ++cnt){ + fluid->_density[cnt] = 0.; + fluid->_manta_flags[cnt] = 2; + } + } + int step = 0; + for (int cnty(0);cnty<fluid->yRes(); ++cnty) + for(int cntz(0);cntz<fluid->zRes(); ++cntz) + { + step = fluid->xRes() + cnty * fluid->xRes() + cntz * fluid->xRes()*fluid->yRes(); + if ((step < 0) || (step > fluid->_totalCells)){ + cout << "UpdatePointers: step is larger tahn cell dim" << step << endl; + } + fluid->_density[step] = manta_fluid_density[cnty + cntz*fluid->xRes()]; + fluid->_manta_flags[step] = manta_fluid_flags[cnty + cntz*fluid->xRes()]; + } + } + else{ + fluid->_density = (float* )pointerFromString(getGridPointer("density", "s")); + fluid->_manta_flags = (int* )pointerFromString(getGridPointer("flags", "s")); + } +// fluid->_density = (float* )pointerFromString(getGridPointer("density", "s")); + + fluid->_manta_inflow = (float* )pointerFromString(getGridPointer("inflow_grid", "s")); + if (fluid-> manta_resoution == 2){return;} + if (fluid->using_colors){ + cout<< "POINTER FOR R_LOW" << fluid->_color_r<< endl; + fluid->_color_r = (float* )pointerFromString(getGridPointer("color_r_low", "s")); + cout<< "POINTER FOR R_LOW" << fluid->_color_r<< endl; + fluid->_color_g = (float* )pointerFromString(getGridPointer("color_g_low", "s")); + fluid->_color_b = (float* )pointerFromString(getGridPointer("color_b_low", "s")); + } + if(fluid->using_heat){ + cout<< "Updating Heat" << fluid->_heat<< endl; + fluid->_heat = (float* )pointerFromString(getGridPointer("heat_low", "s")); + cout<< "Updating Heat" << fluid->_heat<< endl; + } +} + +void Manta_API::updateHighResPointers(WTURBULENCE *wt, bool updateColor) +{ + wt->_densityBig = (float* )pointerFromString(getGridPointer("xl_density", "xl"));; + if (updateColor){ + cout<< "POINTER FOR R_HIGH" << wt->_color_rBig << endl; + wt->_color_rBig = (float* )pointerFromString(getGridPointer("color_r_high", "xl")); + cout<< "POINTER FOR R_HIGH" << wt->_color_rBig << endl; + wt->_color_gBig = (float* )pointerFromString(getGridPointer("color_g_high", "xl")); + wt->_color_bBig = (float* )pointerFromString(getGridPointer("color_b_high", "xl")); + } +} + diff --git a/intern/smoke/intern/MANTA.h b/intern/smoke/intern/MANTA.h new file mode 100644 index 00000000000..fad858a5983 --- /dev/null +++ b/intern/smoke/intern/MANTA.h @@ -0,0 +1,136 @@ +#ifndef MANTA_H +#define MANTA_H +#include "FLUID_3D.h" +#include "zlib.h" +#include "../../../source/blender/makesdna/DNA_scene_types.h" +#include "../../../source/blender/makesdna/DNA_modifier_types.h" +#include "../../../source/blender/makesdna/DNA_smoke_types.h" +#include <sstream> +#include <stdlib.h> +#include <fstream> +#include <pthread.h> +#include <Python.h> +#include <vector> + +#include "BLI_path_util.h" + +#ifdef WIN32 +#include "BLI_winstuff.h" +#endif + +void export_force_fields(int size_x, int size_y, int size_z, float *f_x, float*f_y, float*f_z);/*defined in pymain.cpp*/ +void export_em_fields(float *em_map, float flow_density, int min_x, int min_y, int min_z, int max_x, int max_y, int max_z, int d_x, int d_y, int d_z, float *inf, float *vel);/*defined in pymain.cpp*/ +extern "C" void manta_write_effectors(struct FLUID_3D *fluid); /*defined in smoke_api.cpp*/ +void runMantaScript(const string& ss,vector<string>& args);//defined in manta_pp/pwrapper/pymain.cpp + +/*for passing to detached thread*/ +struct manta_arg_struct { + Scene s; + SmokeModifierData smd; +}; + +static bool manta_sim_running=true; + +extern "C" bool manta_check_grid_size(struct FLUID_3D *fluid, int dimX, int dimY, int dimZ); + +extern "C" int read_mantaflow_sim(struct SmokeDomainSettings *sds, char *name, bool read_wavelets); + +class Manta_API{ +private: + Manta_API() {} + Manta_API(const Manta_API &); + Manta_API & operator=(const Manta_API &); +public: + ~Manta_API(); + Manta_API(int *res, float dx, float dtdef, int init_heat, int init_fire, int init_colors, struct SmokeDomainSettings *sds); + void initBlenderRNA(float *alpha, float *beta, float *dt_factor, float *vorticity, int *border_colli, float *burning_rate, + float *flame_smoke, float *flame_smoke_color, float *flame_vorticity, float *ignition_temp, float *max_temp); + int _totalCells; + int _xRes, _yRes, _zRes; + float _res; + int _slabSize; + float _dt,_dx; + float* _density; + float* _xVelocity; + float* _yVelocity; + float* _zVelocity; + float* _xVelocityOb; + float* _yVelocityOb; + float* _zVelocityOb; + float* _xForce; + float* _yForce; + float* _zForce; + float *_alpha; // for the buoyancy density term <-- as pointer to get blender RNA in here + float *_beta; // was _buoyancy <-- as pointer to get blender RNA in here + + float *_dtFactor; + float *_vorticityRNA; // RNA-pointer. + int *_borderColli; // border collision rules <-- as pointer to get blender RNA in here + float *_burning_rate; // RNA pointer + float *_flame_smoke; // RNA pointer + float *_flame_smoke_color; // RNA pointer + float *_flame_vorticity; // RNA pointer + float *_ignition_temp; // RNA pointer + float *_max_temp; // RNA pointer + + unsigned char* _obstacles; /* only used (useful) for static obstacles like domain */ + void step(float dt, float gravity[3]); +// void runMantaScript(const string&, vector<string>& args);//defined in manta_pp/pwrapper/pymain.cpp + + void indent_ss(stringstream& ss, int indent); + + void manta_gen_noise(stringstream& ss, char* solver, int indent, char *noise, int seed, bool load, bool clamp, float clampNeg, float clampPos, float valScale, float valOffset, float timeAnim); + + void manta_solve_pressure(stringstream& ss, char *flags, char *vel, char *pressure, bool useResNorms, int openBound, int solver_res,float cgMaxIterFac=1.0, float cgAccuracy = 0.01); + + void manta_advect_SemiLagr(stringstream& ss, int indent, char *flags, char *vel, char *grid, int order); + + /*create solver, handle 2D case*/ + void manta_create_solver(stringstream& ss, char *name, char *nick, char *grid_size_name, int x_res, int y_res, int z_res, int dim); + + inline bool file_exists (const std::string& name); + + /*blender transforms obj coords to [-1,1]. This method transforms them back*/ + void add_mesh_transform_method(stringstream& ss); + + void manta_cache_path(char *filepath); + + void create_manta_folder(); + + void *run_manta_scene_thread(void *threadid); + + void run_manta_sim_highRes(WTURBULENCE *wt); + + void run_manta_scene(Manta_API * fluid); + + void stop_manta_sim(); + + static void generate_manta_sim_file_highRes(SmokeModifierData *smd); + + void manta_sim_step(int frame); + + static std::string getRealValue(const string& varName, SmokeModifierData *sds); + + static std::string parseLine(const string& line, SmokeModifierData *sds); + + static std::string parseScript(const string& setup_string, SmokeModifierData *sds); + + pthread_t manta_thread; + + static void * pointerFromString(const std::string& s); + + static string gridNameFromType(const string& type); + static void addGrid(void * data,string name, string type, int x, int y, int z, bool is2D); + static void addAdaptiveGrid(void * data, string gridName, string solverName, string type,int minX, int minY, int minZ, int maxX, int maxY, int maxZ, bool is2D); + static void export_obstacles(float *data, int x, int y, int z, bool is2D); + + static std::string getGridPointer(string gridName, string solverName); + static void updatePointers(FLUID_3D *fluid, bool updateColor); + static void updateHighResPointers(WTURBULENCE *wt,bool updateColor); + static void manta_export_grids(SmokeModifierData *smd); +}; + + + +#endif /* MANTA_H */ + diff --git a/intern/smoke/intern/WTURBULENCE.cpp b/intern/smoke/intern/WTURBULENCE.cpp index 3d712d2124a..000a78e03b8 100644 --- a/intern/smoke/intern/WTURBULENCE.cpp +++ b/intern/smoke/intern/WTURBULENCE.cpp @@ -37,6 +37,7 @@ #include "SPHERE.h" #include <zlib.h> #include <math.h> +#include "scenarios/smoke.h" // needed to access static advection functions #include "FLUID_3D.h" @@ -48,6 +49,12 @@ // 2^ {-5/6} static const float persistence = 0.56123f; +#ifdef WITH_MANTA +#include "MANTA.h" +#endif + +#ifndef WITH_MANTA /*old WTurbulence Solver*/ + ////////////////////////////////////////////////////////////////////// // constructor ////////////////////////////////////////////////////////////////////// @@ -1196,3 +1203,182 @@ void WTURBULENCE::stepTurbulenceFull(float dtOrg, float* xvel, float* yvel, floa _totalStepsBig++; } + + + + + + +#else /*USING MANTAFLOW WTURBULENCE*/ + +WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors,SmokeDomainSettings *sds) +{ + // if noise magnitude is below this threshold, its contribution + // is negilgible, so stop evaluating new octaves + _cullingThreshold = 1e-3; + + // factor by which to increase the simulation resolution + _amplify = amplify; + + // manually adjust the overall amount of turbulence + // DG - RNA-fied _strength = 2.; + + // add the corresponding octaves of noise + _octaves = (int)(log((float)_amplify) / log(2.0f) + 0.5f); // XXX DEBUG/ TODO: int casting correct? - dg + + // noise resolution + _xResBig = _amplify * xResSm; + _yResBig = _amplify * yResSm; + _zResBig = _amplify * zResSm; + _resBig = Vec3Int(_xResBig, _yResBig, _zResBig); + _invResBig = Vec3(1.0f/(float)_resBig[0], 1.0f/(float)_resBig[1], 1.0f/(float)_resBig[2]); + _slabSizeBig = _xResBig*_yResBig; + _totalCellsBig = _slabSizeBig * _zResBig; + + // original / small resolution + _xResSm = xResSm; + _yResSm = yResSm; + _zResSm = zResSm; + _resSm = Vec3Int(xResSm, yResSm, zResSm); + _invResSm = Vec3(1.0f/(float)_resSm[0], 1.0f/(float)_resSm[1], 1.0f/(float)_resSm[2] ); + _slabSizeSm = _xResSm*_yResSm; + _totalCellsSm = _slabSizeSm * _zResSm; + + // allocate high resolution density field + _totalStepsBig = 0; + _densityBig = NULL; + _densityBigOld = new float[_totalCellsBig]; + + for(int i = 0; i < _totalCellsBig; i++) { + _densityBigOld[i] = 0.; + } + + /* fire */ + _flameBig = _fuelBig = _fuelBigOld = NULL; + _reactBig = _reactBigOld = NULL; + if (init_fire) { + initFire(); + } + /* colors */ + _color_rBig = _color_rBigOld = NULL; + _color_gBig = _color_gBigOld = NULL; + _color_bBig = _color_bBigOld = NULL; + using_colors = false; + if (init_colors) { + using_colors = true; + initColors(0.0f, 0.0f, 0.0f); + } + + // allocate & init texture coordinates + _tcU = new float[_totalCellsSm]; + _tcV = new float[_totalCellsSm]; + _tcW = new float[_totalCellsSm]; + _tcTemp = new float[_totalCellsSm]; + + // map all + const float dx = 1.0f/(float)(_resSm[0]); + const float dy = 1.0f/(float)(_resSm[1]); + const float dz = 1.0f/(float)(_resSm[2]); + int index = 0; + for (int z = 0; z < _zResSm; z++) + for (int y = 0; y < _yResSm; y++) + for (int x = 0; x < _xResSm; x++, index++) + { + _tcU[index] = x*dx; + _tcV[index] = y*dy; + _tcW[index] = z*dz; + _tcTemp[index] = 0.; + } + + // noise tiles + _noiseTile = new float[noiseTileSize * noiseTileSize * noiseTileSize]; + setNoise(noisetype, noisefile_path); + sds->smd->domain->wt = this; + Manta_API::generate_manta_sim_file_highRes(sds->smd); + Manta_API::updateHighResPointers(this,using_colors); +} +/// destructor +WTURBULENCE::~WTURBULENCE() +{ + delete[] _densityBig; + delete[] _densityBigOld; + if (_flameBig) delete[] _flameBig; + if (_fuelBig) delete[] _fuelBig; + if (_fuelBigOld) delete[] _fuelBigOld; + if (_reactBig) delete[] _reactBig; + if (_reactBigOld) delete[] _reactBigOld; + + if (_color_rBig) delete[] _color_rBig; + if (_color_rBigOld) delete[] _color_rBigOld; + if (_color_gBig) delete[] _color_gBig; + if (_color_gBigOld) delete[] _color_gBigOld; + if (_color_bBig) delete[] _color_bBig; + if (_color_bBigOld) delete[] _color_bBigOld; + + delete[] _tcU; + delete[] _tcV; + delete[] _tcW; + delete[] _tcTemp; + + delete[] _noiseTile; +} + +void WTURBULENCE::initFire(){} +void WTURBULENCE::initColors(float init_r, float init_g, float init_b) +{ + if (!_color_rBig){ + using_colors = true; + PyGILState_STATE gilstate = PyGILState_Ensure(); + stringstream ss; + ss << "manta_color_r = " << init_r << endl; + ss << "manta_color_g = " << init_g << endl; + ss << "manta_color_b = " << init_b << endl; + PyRun_SimpleString(ss.str().c_str()); + PyRun_SimpleString(smoke_init_colors_high.c_str()); + PyGILState_Release(gilstate); + Manta_API::updateHighResPointers(this,true); + } +} + +void WTURBULENCE::setNoise(int type, const char *noisefile_path){} +void WTURBULENCE::initBlenderRNA(float *strength){} + +// step more readable version -- no rotation correction +void WTURBULENCE::stepTurbulenceReadable(float dt, float* xvel, float* yvel, float* zvel, unsigned char *obstacles){ + PyGILState_STATE gilstate = PyGILState_Ensure(); + int sim_frame = 1; + // manta_write_effectors(fluid); + std::string frame_str = static_cast<ostringstream*>( &(ostringstream() << sim_frame) )->str(); + std::string py_string_0 = string("sim_step_high(").append(frame_str); + std::string py_string_1 = py_string_0.append(")\0"); + PyRun_SimpleString(py_string_1.c_str()); + PyGILState_Release(gilstate); + Manta_API::updateHighResPointers(this,using_colors); +} + +// step more complete version -- include rotation correction +// and use OpenMP if available +void WTURBULENCE::stepTurbulenceFull(float dt, float* xvel, float* yvel, float* zvel, unsigned char *obstacles){ + PyGILState_STATE gilstate = PyGILState_Ensure(); + int sim_frame = 1; + // manta_write_effectors(fluid); + std::string frame_str = static_cast<ostringstream*>( &(ostringstream() << sim_frame) )->str(); + std::string py_string_0 = string("sim_step_high(").append(frame_str); + std::string py_string_1 = py_string_0.append(")\0"); + PyRun_SimpleString(py_string_1.c_str()); + PyGILState_Release(gilstate); + Manta_API::updateHighResPointers(this,using_colors); +} + +// texcoord functions +void WTURBULENCE::advectTextureCoordinates(float dtOrg, float* xvel, float* yvel, float* zvel, float *tempBig1, float *tempBig2){} +void WTURBULENCE::resetTextureCoordinates(float *_eigMin, float *_eigMax){} + +void WTURBULENCE::computeEnergy(float *energy, float* xvel, float* yvel, float* zvel, unsigned char *obstacles){} + +void WTURBULENCE::computeEigenvalues(float *_eigMin, float *_eigMax){} +void WTURBULENCE::decomposeEnergy(float *energy, float *_highFreqEnergy){} +Vec3 WTURBULENCE::WVelocity(Vec3 p){return Vec3(0.);} +Vec3 WTURBULENCE::WVelocityWithJacobian(Vec3 p, float* xUnwarped, float* yUnwarped, float* zUnwarped){return Vec3(0.);} + +#endif diff --git a/intern/smoke/intern/WTURBULENCE.h b/intern/smoke/intern/WTURBULENCE.h index 36635325f62..787177bbb81 100644 --- a/intern/smoke/intern/WTURBULENCE.h +++ b/intern/smoke/intern/WTURBULENCE.h @@ -36,7 +36,7 @@ struct WTURBULENCE { public: // both config files can be NULL, altCfg might override values from noiseCfg - WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors); + WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors,struct SmokeDomainSettings *sds); /// destructor virtual ~WTURBULENCE(); @@ -128,7 +128,8 @@ struct WTURBULENCE float* _color_gBigOld; float* _color_bBig; float* _color_bBigOld; - + bool using_colors; + // texture coordinates for noise float* _tcU; float* _tcV; diff --git a/intern/smoke/intern/scenarios/smoke.h b/intern/smoke/intern/scenarios/smoke.h new file mode 100644 index 00000000000..91eb27a13ba --- /dev/null +++ b/intern/smoke/intern/scenarios/smoke.h @@ -0,0 +1,361 @@ +#include <string> +using namespace std; +const string smoke_clean = ""; + +const string smoke_setup_low ="from manta import *\n\ +import os, shutil, math, sys\n\ +def transform_back(obj, gs):\n\ + obj.scale(gs/2)\n\ + obj.offset(gs/2)\n\ +\n\ +def load_once(grid, file, dict):\n\ + if grid not in dict:\n\ + print('Loading file' + file + 'in grid')\n\ + grid.load(file)\n\ + dict[grid] = 1\n\ +# solver params\n\ +res = $RES$\n\ +solver_dim = $SOLVER_DIM$\n\ +gs = vec3($RESX$,$RESY$,$RESZ$)\n\ +boundConditions = '$BOUNDCONDITIONS$'\n\ +if solver_dim == 2:\n\ + gs.z = 1\n\ +s = FluidSolver(name='main', gridSize = gs, dim = $SOLVER_DIM$)\n\ +s.timestep = 0.1\n\ +timings = Timings()\n\ +\n\ +# prepare grids\n\ +flags = s.create(FlagGrid)\n\ +vel = s.create(MACGrid)\n\ +density = s.create(LevelsetGrid)\n\ +pressure = s.create(RealGrid)\n\ +\n\ +# noise field\n\ +#noise = s.create(NoiseField, loadFromFile=True)\n\ +#noise.posScale = vec3(45)\n\ +#noise.clamp = True\n\ +#noise.clampNeg = 0\n\ +#noise.clampPos = 1\n\ +#noise.valScale = 1\n\ +#noise.valOffset = 0.75\n\ +#noise.timeAnim = 0.2\n\ +\n\ +flags.initDomain()\n\ +flags.fillGrid()\n\ +\n\ +inflow_grid = s.create(LevelsetGrid)\n\ +source = s.create(Mesh)\n\ +forces = s.create(MACGrid)\n\ +dict_loaded = dict()\n\ +manta_using_colors = False\n\ +manta_using_heat = False\n\ +low_flags_updated = False\n\ +"; + +const string smoke_setup_high = "xl_gs = vec3($HRESX$, $HRESY$, $HRESZ$) \n\ +xl = Solver(name = 'larger', gridSize = xl_gs) \n\ +uvs =$UVS_CNT$\n\ +if $USE_WAVELETS$:\n\ + upres = $UPRES$\n\ + wltStrength = $WLT_STR$\n\ + if $UPRES$ > 0:\n\ + octaves = int( math.log(upres)/ math.log(2.0) + 0.5 ) \n\ + else:\n\ + octaves = 0\n\ +if $USE_WAVELETS$ and $UPRES$ > 0:\n\ + xl.timestep = $XL_TIMESTEP$ \n\ + xl_vel = xl.create(MACGrid) \n\ + xl_density = xl.create(RealGrid) \n\ + xl_flags = xl.create(FlagGrid) \n\ + xl_flags.initDomain() \n\ + xl_flags.fillGrid() \n\ + xl_noise = xl.create(NoiseField, fixedSeed=256, loadFromFile=True) \n\ + xl_noise.posScale = vec3(20) \n\ + xl_noise.clamp = False \n\ + xl_noise.clampNeg = $NOISE_CN$ \n\ + xl_noise.clampPos = $NOISE_CP$ \n\ + xl_noise.valScale = $NOISE_VALSCALE$ \n\ + xl_noise.valOffset = $NOISE_VALOFFSET$ \n\ + xl_noise.timeAnim = $NOISE_TIMEANIM$ * $UPRES$ \n\ + xl_wltnoise = xl.create(NoiseField, loadFromFile=True) \n\ + xl_wltnoise.posScale = vec3( int(1.0*gs.x) ) * 0.5 \n\ + xl_wltnoise.posScale = xl_wltnoise.posScale * 0.5\n\ + xl_wltnoise.timeAnim = 0.1 \n\ +"; + +const string smoke_init_colors_low = "print(\"INitializing Colors\")\n\ +color_r_low = s.create(RealGrid)\n\ +color_g_low = s.create(RealGrid)\n\ +color_b_low = s.create(RealGrid)\n\ +color_r_low.add(density) \n\ +color_r_low.multConst(manta_color_r) \n\ +\n\ +color_g_low.add(density) \n\ +color_g_low.multConst(manta_color_g) \n\ +\n\ +color_b_low.add(density) \n\ +color_b_low.multConst(manta_color_b) \n\ +manta_using_colors = True\n"; + +const string smoke_del_colors_low = "\n\ +del color_r_low \n\ +del color_g_low \n\ +del color_b_low \n\ +manta_using_colors = False"; + +const string smoke_init_colors_high = "print(\"INitializing Colors highres\")\n\ +color_r_high = xl.create(RealGrid)\n\ +color_g_high = xl.create(RealGrid)\n\ +color_b_high = xl.create(RealGrid)\n\ +color_r_high.add(xl_density) \n\ +color_r_high.multConst(manta_color_r) \n\ +\n\ +color_g_high.add(xl_density) \n\ +color_g_high.multConst(manta_color_g) \n\ +\n\ +color_b_high.add(xl_density) \n\ +color_b_high.multConst(manta_color_b) \n\ +manta_using_colors = True\n"; + +const string smoke_init_heat_low = "print(\"INitializing heat lowres\")\n\ +heat_low = s.create(RealGrid)\n\ +manta_using_heat = True\n"; + +const string smoke_del_colors_high = "\n\ +del color_r_high \n\ +del color_g_high \n\ +del color_b_high \n\ +manta_using_colors = False"; + +const string smoke_export_low = "\n\ +import os\n\ +density.save(os.path.join('$MANTA_EXPORT_PATH$','density.uni'))\n\ +flags.save(os.path.join('$MANTA_EXPORT_PATH$','flags.uni'))\n\ +inflow_grid.save(os.path.join('$MANTA_EXPORT_PATH$','inflow.uni'))\n\ +forces.save(os.path.join('$MANTA_EXPORT_PATH$','forces.uni'))\n\ +print('Grids exported')"; + +const string standalone = "\ +if (GUI):\n\ + gui =Gui()\n\ + gui.show()\n\ +\n\ +for step in range(100):\n\ + sim_step_low(step, True)\n"; + +const string smoke_step_low = "def sim_step_low(t, standalone = False):\n\ + #applying inflow\n\ + #if standalone and t==0:\n\ + # density.load('density.uni')\n\ + # flags.load('flags.uni')\n\ + # forces.load('forces.uni')\n\ + #if standalone:\n\ + # inflow_grid.load('inflow.uni')\n\ + # inflow_grid.multConst(0.1)\n\ + # density.add(inflow_grid)\n\ + #elif solver_dim == 2:\n\ + # density.add(inflow_grid)\n\ + print ('Simulating frame ' + str(t))\n\ + if not standalone and t == 1 and solver_dim == 2:\n\ + density.add(inflow_grid)\n\ + if manta_using_heat:\n\ + gravity=vec3(0,0,-0.0981) if solver_dim==3 else vec3(0,-0.0981,0)\n\ + addHeatBuoyancy(density=density, densCoeff = 0.001, vel=vel, gravity=gravity, flags=flags, heat = heat_low, heatCoeff = -0.1*10)\n\ + else:\n\ + gravity=vec3(0,0,-0.01) if solver_dim==3 else vec3(0,-0.01,0)\n\ + addBuoyancy(density=density, vel=vel, gravity=gravity, flags=flags)\n\ + if manta_using_colors:\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=color_r_low, order=$ADVECT_ORDER$)\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=color_g_low, order=$ADVECT_ORDER$)\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=color_b_low, order=$ADVECT_ORDER$)\n\ + print ('Advecting density')\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=density, order=$ADVECT_ORDER$)\n\ + print ('Advecting velocity')\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=vel , order=$ADVECT_ORDER$, strength=1.0)\n\ + \n\ + print ('Walls')\n\ + setWallBcs(flags=flags, vel=vel) \n\ + print ('vorticity')\n\ + if $VORTICITY$ > 0.01:\n\ + vorticityConfinement( vel=vel, flags=flags, strength=$VORTICITY$ ) \n\ + print ('forcefield')\n\ + addForceField(flags=flags, vel=vel,force=forces)\n\ + forces.clear()\n\ + \n\ + print ('pressure')\n\ + solvePressure(flags=flags, vel=vel, pressure=pressure, openBound=boundConditions)\n\ + print ('walls')\n\ + setWallBcs(flags=flags, vel=vel)\n\ + \n\ + s.step()\n"; + +const string liquid_step_low = "def sim_step_low(t):\n\ +#update flags form density on first step\n\ + setWallBcs(flags=flags, vel=vel)\n\ + density.multConst(-1.)\n\ + print (manta_using_colors)\n\ + global low_flags_updated\n\ + if not low_flags_updated:\n\ + print ('Updating Flags from Levelset on startup!')\n\ + flags.updateFromLevelset(density)\n\ + low_flags_updated = True \n\ + setWallBcs(flags=flags, vel=vel)\n\ + density.reinitMarching(flags=flags, velTransport=vel)\n\ + advectSemiLagrange(flags=flags, vel=vel, grid=density, order=2)\n\ + flags.updateFromLevelset(density)\n\ + \n\ + advectSemiLagrange(flags=flags, vel=vel, grid=vel, order=2)\n\ + addGravity(flags=flags, vel=vel, gravity=vec3(0,0,-0.981))\n\ + \n\ + # print current maximal velocity\n\ + maxvel = vel.getMaxValue()\n\ + print ('Current max velocity %f ' % maxvel)\n\ + \n\ + # pressure solve\n\ + setWallBcs(flags=flags, vel=vel)\n\ + solvePressure(flags=flags, vel=vel, pressure=pressure, cgMaxIterFac=0.5, useResNorm=True) \n\ + setWallBcs(flags=flags, vel=vel)\n\ + s.step()\n\ + density.multConst(-1.)\n\ +"; + +const string smoke_step_high = "def sim_step_high(t):\n\ + interpolateMACGrid( source=vel, target=xl_vel ) \n\ + sStr = 1.0 * wltStrength \n\ + sPos = 2.0 \n\ + for o in range(octaves): \n\ + for i in range(uvs): \n\ + uvWeight = getUvWeight(uv[i]) \n\ + applyNoiseVec3( flags=xl_flags, target=xl_vel, noise=xl_wltnoise, scale=sStr * uvWeight, scaleSpatial=sPos , weight=energy, uv=uv[i] ) \n\ + sStr *= 0.06 # magic kolmogorov factor \n\ + sPos *= 2.0 \n\ + for substep in range(upres): \n\ + advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=xl_density, order=$ADVECT_ORDER$) \n\ + if manta_using_colors:\n\ + advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=color_r_high, order=$ADVECT_ORDER$)\n\ + advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=color_g_high, order=$ADVECT_ORDER$)\n\ + advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=color_b_high, order=$ADVECT_ORDER$)\n\ +\n\ + xl.step()\n"; + +const string full_smoke_setup = "from manta import * \n\ +import os, shutil, math, sys \n\ +def transform_back(obj, gs):\n\ + obj.scale(gs/2)\n\ + obj.offset(gs/2)\n\ +\n\ +uvs = $UVS_CNT$\n\ +solver_dim = $SOLVER_DIM$\n\ +velInflow = vec3(0, 0, 1)\n\ +if $USE_WAVELETS$:\n\ + upres = $UPRES$\n\ + wltStrength = $WLT_STR$\n\ + if $UPRES$ > 0:\n\ + octaves = int( math.log(upres)/ math.log(2.0) + 0.5 ) \n\ + else:\n\ + octaves = 0\n\ +res = $RES$\n\ +gs = vec3($RESX$, $RESY$, $RESZ$) \n\ +s = Solver(name = 'main', gridSize = gs, dim = solver_dim) \n\ +s.timestep = $TIMESTEP$ \n\ +noise = s.create(NoiseField, fixedSeed=256, loadFromFile=True) \n\ +noise.posScale = vec3(20) \n\ +noise.clamp = False \n\ +noise.clampNeg = $NOISE_CN$\n\ +noise.clampPos = $NOISE_CP$\n\ +noise.valScale = $NOISE_VALSCALE$\n\ +noise.valOffset = $NOISE_VALOFFSET$\n\ +noise.timeAnim = $NOISE_TIMEANIM$ \n\ +source = s.create(Mesh)\n\ +source.load('manta_flow.obj')\n\ +transform_back(source, gs)\n\ +sourceVel = s.create(Mesh)\n\ +sourceVel.load('manta_flow.obj')\n\ +transform_back(sourceVel, gs)\n\ +xl_gs = vec3($HRESX$, $HRESY$, $HRESZ$) \n\ +xl = Solver(name = 'larger', gridSize = xl_gs, dim = solver_dim) \n\ +if $USE_WAVELETS$ and $UPRES$ > 0:\n\ + xl.timestep = $XL_TIMESTEP$ \n\ + xl_vel = xl.create(MACGrid) \n\ + xl_density = xl.create(RealGrid) \n\ + xl_flags = xl.create(FlagGrid) \n\ + xl_flags.initDomain() \n\ + xl_flags.fillGrid() \n\ + xl_source = s.create(Mesh)\n\ + xl_source.load('manta_flow.obj')\n\ + transform_back(xl_source, gs)\n\ + xl_noise = xl.create(NoiseField, fixedSeed=256, loadFromFile=True) \n\ + xl_noise.posScale = vec3(20) \n\ + xl_noise.clamp = False \n\ + xl_noise.clampNeg = $NOISE_CN$ \n\ + xl_noise.clampPos = $NOISE_CP$ \n\ + xl_noise.valScale = $NOISE_VALSCALE$ \n\ + xl_noise.valOffset = $NOISE_VALOFFSET$ \n\ + xl_noise.timeAnim = $NOISE_TIMEANIM$ * $UPRES$ \n\ +flags = s.create(FlagGrid) \n\ +flags.initDomain() \n\ +flags.fillGrid() \n\ +uv = [] \n\ +for i in range(uvs): \n\ + uvGrid = s.create(VecGrid) \n\ + uv.append(uvGrid) \n\ + resetUvGrid( uv[i] ) \n\ +vel = s.create(MACGrid) \n\ +density = s.create(RealGrid) \n\ +pressure = s.create(RealGrid) \n\ +energy = s.create(RealGrid) \n\ +tempFlag = s.create(FlagGrid)\n\ +sdf_flow = s.create(LevelsetGrid)\n\ +forces = s.create(MACGrid)\n\ +source.meshSDF(source, sdf_flow, 1.1)\n\ +source_shape = s.create(Cylinder, center=gs*vec3(0.5,0.1,0.5), radius=res*0.14, z=gs*vec3(0, 0.02, 0))\n\ +xl_wltnoise = s.create(NoiseField, loadFromFile=True) \n\ +xl_wltnoise.posScale = vec3( int(1.0*gs.x) ) * 0.5 \n\ +xl_wltnoise.posScale = xl_wltnoise.posScale * 0.5\n\ +xl_wltnoise.timeAnim = 0.1 \n\ +\n\ +\n\ +def sim_step(t):\n\ + forces.load('manta_forces.uni')\n\ + addForceField(flags=flags, vel=vel,force=forces)\n\ + addBuoyancy(density=density, vel=vel, gravity=vec3($BUYO_X$,$BUYO_Y$,$BUYO_Z$), flags=flags) \n\ + advectSemiLagrange(flags=flags, vel=vel, grid=density, order=$ADVECT_ORDER$) \n\ + advectSemiLagrange(flags=flags, vel=vel, grid=vel, order=$ADVECT_ORDER$) \n\ + for i in range(uvs): \n\ + advectSemiLagrange(flags=flags, vel=vel, grid=uv[i], order=$ADVECT_ORDER$) \n\ + updateUvWeight( resetTime=16.5 , index=i, numUvs=uvs, uv=uv[i] )\n\ + applyInflow=False\n\ + if (t>=0 and t<75):\n\ + densityInflowMesh(flags=flags, density=density, mesh=source, value=1)\n\ + applyInflow=True\n\ + setWallBcs(flags=flags, vel=vel) \n\ + vorticityConfinement( vel=vel, flags=flags, strength=0.2 ) \n\ + solvePressure(flags=flags, vel=vel, pressure=pressure, useResNorm=True, openBound='xXyYzZ', cgMaxIterFac=1, cgAccuracy=0.01) \n\ + setWallBcs(flags=flags, vel=vel) \n\ + computeEnergy(flags=flags, vel=vel, energy=energy)\n\ + tempFlag.copyFrom(flags)\n\ + extrapolateSimpleFlags( flags=flags, val=tempFlag, distance=2, flagFrom=FlagObstacle, flagTo=FlagFluid )\n\ + extrapolateSimpleFlags( flags=tempFlag, val=energy, distance=6, flagFrom=FlagFluid, flagTo=FlagObstacle )\n\ + computeWaveletCoeffs(energy)\n\ + print(\"Writing Grid to \" + $DENSITY_MEM$ + \"with size\" + $DENSITY_SIZE$)\n\ + density.writeGridToMemory(memLoc = $DENSITY_MEM$,sizeAllowed = $DENSITY_SIZE$)\n\ + density.save('den%04d_temp.uni' % t) \n\ + os.rename('den%04d_temp.uni' % t, 'den%04d.uni' % t) \n\ + s.step()\n\ + \n\ + interpolateMACGrid( source=vel, target=xl_vel ) \n\ + sStr = 1.0 * wltStrength \n\ + sPos = 2.0 \n\ + for o in range(octaves): \n\ + for i in range(uvs): \n\ + uvWeight = getUvWeight(uv[i]) \n\ + applyNoiseVec3( flags=xl_flags, target=xl_vel, noise=xl_wltnoise, scale=sStr * uvWeight, scaleSpatial=sPos , weight=energy, uv=uv[i] ) \n\ + sStr *= 0.06 # magic kolmogorov factor \n\ + sPos *= 2.0 \n\ + for substep in range(upres): \n\ + advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=xl_density, order=$ADVECT_ORDER$) \n\ + if (applyInflow): \n\ + densityInflowMesh(flags=xl_flags, density=xl_density, mesh=source, value=1)\n\ + xl_density.save('densityXl_%04d.uni' % t)\n\ + xl.step()\n\ +"; diff --git a/intern/smoke/intern/smoke_API.cpp b/intern/smoke/intern/smoke_API.cpp index d79aaf76d56..462af5b12b4 100644 --- a/intern/smoke/intern/smoke_API.cpp +++ b/intern/smoke/intern/smoke_API.cpp @@ -35,19 +35,24 @@ #include <stdio.h> #include <stdlib.h> #include <math.h> - +#include "MANTA.h" +#include "../../../source/blender/python/manta_pp/util/vectorbase.h" #include "../extern/smoke_API.h" /* to ensure valid prototypes */ -extern "C" FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, int use_fire, int use_colors) +extern "C" int *smoke_get_manta_flags(struct FLUID_3D *fluid){ + return fluid->_manta_flags; +} + +extern "C" FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, int use_fire, int use_colors, struct SmokeModifierData *smd ) { - FLUID_3D *fluid = new FLUID_3D(res, dx, dtdef, use_heat, use_fire, use_colors); + FLUID_3D *fluid = new FLUID_3D(res, dx, dtdef, use_heat, use_fire, use_colors,smd); return fluid; } -extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors) +extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors,struct SmokeDomainSettings *sds) { if (amplify) - return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors); + return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors,sds); else return NULL; } @@ -74,6 +79,12 @@ extern "C" size_t smoke_get_index2d(int x, int max_x, int y /*, int max_y, int z return x + y * max_x; } +extern "C" void smoke_manta_export(SmokeModifierData *smd) +{ + if (!smd) return; + Manta_API::manta_export_grids(smd); +} + extern "C" void smoke_step(FLUID_3D *fluid, float gravity[3], float dtSubdiv) { if (fluid->_fuel) { @@ -233,6 +244,11 @@ extern "C" float *smoke_get_density(FLUID_3D *fluid) return fluid->_density; } +extern "C" float *smoke_get_inflow_grid(FLUID_3D *fluid) +{ + return fluid->_manta_inflow; +} + extern "C" float *smoke_get_fuel(FLUID_3D *fluid) { return fluid->_fuel; @@ -506,3 +522,96 @@ extern "C" void smoke_ensure_colors(FLUID_3D *fluid, WTURBULENCE *wt, float init wt->initColors(init_r, init_g, init_b); } } + + +/*MantaFlow funcs*/ +extern "C" int smoke_mantaflow_read(struct SmokeDomainSettings *sds, char* name, bool with_wavelets) +{ + return read_mantaflow_sim(sds, name, with_wavelets); +} + + +extern "C" void manta_write_effectors(struct FLUID_3D *fluid) +{ + int size_x = fluid->_xRes; + int size_y = fluid->_yRes; + int size_z = fluid->_zRes; + + float *force_x = smoke_get_force_x(fluid); + float *force_y = smoke_get_force_y(fluid); + float *force_z = smoke_get_force_z(fluid); +// export_force_fields(size_x, size_y, size_z, force_x, force_y, force_z); + /*accumulate all force fields in one grid*/ + Manta::Vec3 * accumulated_force = NULL; + long index(0); + if (fluid->manta_resoution == 3){ + accumulated_force = (Manta::Vec3*)calloc(size_x * size_y * size_z , sizeof(Manta::Vec3)); + for (int z(0); z < size_z; z++){ + for (int y(0); y < size_y; y++){ + for (int x(0); x < size_x; x++){ + index = smoke_get_index(x, size_x, y, size_y, z); + accumulated_force[index] = Manta::Vec3(force_x[index], force_y[index], force_z[index]); + } + } + } + } + else if (fluid->manta_resoution == 2){ + accumulated_force = (Manta::Vec3*)malloc(size_x * size_z * sizeof(Manta::Vec3)); + int step(0); + for (int x(0); x < size_x; x++){ + for (int z(0); z < size_z; z++){ + index = smoke_get_index(x, size_x, size_y/2, size_y, z); + accumulated_force[x + z * size_x] = Manta::Vec3(force_x[index], force_z[index], 0.0); + } + } + } + else{ + cout << "ERROR: Manta solver resoltion is neither 2 nor 3; Cannot write forces"<<endl; + return; + } + + + bool is2D = (fluid->manta_resoution == 2); + Manta_API::addGrid(accumulated_force, "forces", "Vec3", size_x, size_y, size_z, is2D); +} + +extern "C" void manta_write_emitters(struct SmokeFlowSettings *sfs, bool highRes, int min_x, int min_y, int min_z, int max_x, int max_y, int max_z, int d_x, int d_y, int d_z,float *influence, float *vel) +{ +// manta_update_effectors(s, smd->domain->manta_obj, smd->domain, 0.1f); + bool is2D = (sfs->smd->domain->fluid->manta_resoution == 2); + if (! highRes) + Manta_API::addAdaptiveGrid(influence, "density", "s", "float", + min_x, min_y, min_z, max_x, max_y, max_z, is2D); + else + Manta_API::addAdaptiveGrid(influence, "xl_density", "xl", "float", min_x, min_y, min_z, max_x, max_y, max_z, is2D); + // export_em_fields(Manta_API::instance()->_emission_map,sfs->density, min_x, min_y, min_z, max_x, max_y, max_z, d_x, d_y, d_z, influence, vel); +} + +/*deprecated*/ +extern "C" void manta_export_obstacles(float * influence, int x, int y, int z) +{ + + cout << "!!!!!!!!!!Deprecated method manta_export_obstacles is being used" << endl; + if (influence == NULL){ + cout<< "ERROR: empty influence object when exporting smoke obstacles" << endl; + return; + } +// Manta_API::export_obstacles(influence, x, y, z); +} + +extern "C" void smoke_mantaflow_stop_sim(struct Manta_API * fluid) +{ + if (fluid == NULL){ + cout<< "ERROR: empty manta_API object when stopping smoke simulation" << endl; + return; + } + fluid->stop_manta_sim(); +} + +extern "C" int cell_index_3D(int index_2d, int sizex,int sizey, int sizez) +{ + + return int(sizey * 0.5) * sizex + + (index_2d % (sizex)) + + int(index_2d/(sizex)) * sizex * sizey; +} diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py index 63268bc6672..c2a2b3be230 100644 --- a/release/scripts/startup/bl_ui/properties_physics_smoke.py +++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py @@ -18,6 +18,8 @@ # <pep8 compliant> import bpy +import os +from copy import deepcopy from bpy.types import Panel from bl_ui.properties_physics_common import ( @@ -204,13 +206,13 @@ class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel): md = context.smoke.domain_settings self.layout.prop(md, "use_adaptive_domain", text="") - + def draw(self, context): layout = self.layout domain = context.smoke.domain_settings layout.active = domain.use_adaptive_domain - + split = layout.split() split.enabled = (not domain.point_cache.is_baked) @@ -327,5 +329,140 @@ class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel): domain = context.smoke.domain_settings effector_weights_ui(self, context, domain.effector_weights, 'SMOKE') +class OBJECT_OT_RunMantaButton(bpy.types.Operator): + bl_idname = "manta_export_scene.button" + bl_label = "Create Python Script and mesh files" + + def execute(self, context): + def silent_remove(filename): + if os.path.exists(filename): + os.remove(filename) + + #need these methods to account for rotated objects + def transform_objgroup(obj_list, domain_obj): + old_scale = deepcopy(domain_obj.scale) + old_loc = deepcopy(domain_obj.location) + #link all objects to new reference- domain + domain_obj.scale = (1,1,1) + domain_obj.location = (0,0,0) + for obj in obj_list: + obj.select = True + obj.location[0] -= old_loc[0] + obj.location[1] -= old_loc[1] + obj.location[2] -= old_loc[2] + obj.constraints.new('CHILD_OF') + obj.constraints.active.target = domain_obj + #scale domain down + domain_obj.scale[0] /= old_scale[0] + domain_obj.scale[1] /= old_scale[1] + domain_obj.scale[2] /= old_scale[2] + return old_scale, old_loc + + def transform_objgroup_back(obj_list, domain_obj, old_data): + old_scale, old_loc = old_data + domain_obj.scale[0] = old_scale[0] + domain_obj.scale[1] = old_scale[1] + domain_obj.scale[2] = old_scale[2] + domain_obj.location[0] = old_loc[0] + domain_obj.location[1] = old_loc[1] + domain_obj.location[2] = old_loc[2] + #remove used constraint and deselect objects + for obj in obj_list: + obj.select = False + obj.constraints.remove(obj.constraints.active) + obj.location[0] += old_loc[0] + obj.location[1] += old_loc[1] + obj.location[2] += old_loc[2] + + coll_objs = [] + flow_objs = [] + selected_before = [] + domain = None + #getting smoke objects + for scene in bpy.data.scenes: + for ob in scene.objects: + for modifier in ob.modifiers: + if modifier.type == 'SMOKE': + if modifier.smoke_type == 'COLLISION': + coll_objs.append(ob) + elif modifier.smoke_type == 'FLOW': + flow_objs.append(ob) + elif modifier.smoke_type == 'DOMAIN' and ob.select: + domain = ob + if ob.select: + selected_before.append(ob) + ob.select = False + + silent_remove("./manta_coll.obj") + silent_remove("./manta_flow.obj") + #exporting here + if coll_objs: + old_data = transform_objgroup(coll_objs, domain) + bpy.ops.export_scene.obj(filepath = "./manta_coll.obj", axis_forward='Y', axis_up='Z', use_selection = True, use_normals = True, use_materials = False, use_triangles = True, group_by_object = True, use_nurbs=True, check_existing= False) + transform_objgroup_back(coll_objs,domain,old_data) + if flow_objs: + old_data = transform_objgroup(flow_objs, domain) + bpy.ops.export_scene.obj(filepath = "./manta_flow.obj", axis_forward='Y', axis_up='Z', use_selection = True, use_normals = True, use_materials = False, use_triangles = True, group_by_object = True, use_nurbs=True, check_existing= False) + transform_objgroup_back(flow_objs,domain,old_data) + for ob in selected_before: + ob.select = True + # ds = domain.modifiers['Smoke'].domain_settings + # if (!global manta_solver_res_switched) and ds.manta_solver_res == 2: + # #resize domain s.th. Y-axis dim corresponds to 1 + # scale_fac = ds.resolution_max / max(domain.scale[0],domain.scale[1],domain.scale[2]) + # domain.scale[1] /= scale_fac + # global manta_solver_res_switched = True + bpy.ops.manta.make_file() + bpy.ops.manta.sim_step() + return{'FINISHED'} + +class OBJECT_OT_StopMantaButton(bpy.types.Operator): + bl_idname = "manta_stop_sim.button" + bl_label = "Stop Mantaflow Simulation" + def execute(self, context): + domain = context.smoke.domain_settings + #setting manta_sim_frame to "stop" value + domain.manta_sim_frame = -1 + return{'FINISHED'} + + +class PHYSICS_PT_smoke_manta_settings(PhysicButtonsPanel, Panel): + bl_label = "MantaFlow Settings" + bl_options = {'DEFAULT_CLOSED'} + name = bpy.props.StringProperty(name="Test Prop", default="Unknown") + StringProp = bpy.props.StringProperty(name="manta_status", description="Status Of Simulation", default="Doing Nothing" ) +# filepath = StringProperty(subtype='FILE_PATH',) + @classmethod + def poll(cls, context): + md = context.smoke + return md and (md.smoke_type == 'DOMAIN') + + def draw_header(self, context): + md = context.smoke.domain_settings + + def draw(self, context): + layout = self.layout + + domain = context.smoke.domain_settings + split = layout.split() + split.prop(domain, "use_manta_liquid", text="Liquid") + split.operator("manta_export_scene.button", text="Create Manta Setup") + split = layout.split() + split.prop(domain, "manta_filepath") + split = layout.split() + col = split.column() + col.prop(domain, "manta_solver_res", text="Solver Resolution") + col.prop(domain, "manta_uvs", text="UVs count") + split = layout.split() + col = split.column() + col.label("Noise Settings") + col.prop(domain, "noise_clamp_neg", text="Clamp Neg") + col.prop(domain, "noise_clamp_pos", text="Clamp Pos") + col.prop(domain, "noise_time_anim", text="Time Anim") + col = split.column() + col.label("") + col.prop(domain, "noise_val_scale", text="Scale") + col.prop(domain, "noise_val_offset", text="Offset") + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a826fe26e68..162089c6e1d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -56,6 +56,7 @@ set(INC set(INC_SYS ${GLEW_INCLUDE_PATH} ${ZLIB_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIRS} ) set(SRC @@ -413,6 +414,10 @@ if(WITH_MOD_SMOKE) add_definitions(-DWITH_SMOKE) endif() +if(WITH_MOD_MANTA) + add_definitions(-DWITH_MANTA) +endif() + if(WITH_MOD_OCEANSIM) add_definitions(-DWITH_OCEANSIM) endif() diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index 47bba5f5537..4897949ae44 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -52,6 +52,7 @@ incs = [ '#/intern/elbeem/extern', '#/intern/iksolver/extern', '#/intern/smoke/extern', +# '#/source/blender/python/manta_pp', '#/intern/atomic', '../avi', '../blenfont', @@ -76,6 +77,9 @@ defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_SMOKE']: defs.append('WITH_SMOKE') + +#if env['WITH_BF_MANTA']: +# defs.append('WITH_MANTA') if env['WITH_BF_FRAMESERVER']: defs.append('WITH_FRAMESERVER') diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index c477aabbb71..2b9f7cf1bc5 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -574,6 +574,17 @@ static void ptcache_smoke_error(void *smoke_v, const char *message) #define SMOKE_CACHE_VERSION "1.04" +static void writeArrToFile(char* name, float* arr, int numElements) +{ + FILE *filePtr; + filePtr = fopen(name,"w"); + int i=0; + for (i = 0; i < numElements; i++) { + fprintf(filePtr, "%f \n", arr[i]); + } + fclose(filePtr); +} + static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v) { SmokeModifierData *smd= (SmokeModifierData *)smoke_v; @@ -587,7 +598,6 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v) ptcache_file_write(pf, &sds->active_fields, 1, sizeof(int)); ptcache_file_write(pf, &sds->res, 3, sizeof(int)); ptcache_file_write(pf, &sds->dx, 1, sizeof(float)); - if (sds->fluid) { size_t res = sds->res[0]*sds->res[1]*sds->res[2]; float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b; @@ -601,7 +611,13 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v) smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat, &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles); ptcache_file_compressed_write(pf, (unsigned char *)sds->shadow, in_len, out, mode); + + /* writeArrToFile("sh.txt", sds->shadow, res); + */ ptcache_file_compressed_write(pf, (unsigned char *)dens, in_len, out, mode); + + /* writeArrToFile("dens.txt", dens, res); +*/ if (fluid_fields & SM_ACTIVE_HEAT) { ptcache_file_compressed_write(pf, (unsigned char *)heat, in_len, out, mode); ptcache_file_compressed_write(pf, (unsigned char *)heatold, in_len, out, mode); @@ -617,8 +633,17 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v) ptcache_file_compressed_write(pf, (unsigned char *)b, in_len, out, mode); } ptcache_file_compressed_write(pf, (unsigned char *)vx, in_len, out, mode); + +/* writeArrToFile("vx.txt", vx, res); +*/ ptcache_file_compressed_write(pf, (unsigned char *)vy, in_len, out, mode); + +/* writeArrToFile("vy.txt", vx, res); +*/ ptcache_file_compressed_write(pf, (unsigned char *)vz, in_len, out, mode); + +/* writeArrToFile("vz.txt", vx, res); +*/ ptcache_file_compressed_write(pf, (unsigned char *)obstacles, (unsigned int)res, out, mode); ptcache_file_write(pf, &dt, 1, sizeof(float)); ptcache_file_write(pf, &dx, 1, sizeof(float)); diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 559e1e05f3f..7b3c84f7235 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -169,6 +169,7 @@ void flame_get_spectrum(unsigned char *UNUSED(spec), int UNUSED(width), float UN #ifdef WITH_SMOKE + void smoke_reallocate_fluid(SmokeDomainSettings *sds, float dx, int res[3], int free_old) { int use_heat = (sds->active_fields & SM_ACTIVE_HEAT); @@ -181,10 +182,13 @@ void smoke_reallocate_fluid(SmokeDomainSettings *sds, float dx, int res[3], int sds->fluid = NULL; return; } - sds->fluid = smoke_init(res, dx, DT_DEFAULT, use_heat, use_fire, use_colors); + sds->fluid = smoke_init(res, dx, DT_DEFAULT, use_heat, use_fire, use_colors, sds->smd); smoke_initBlenderRNA(sds->fluid, &(sds->alpha), &(sds->beta), &(sds->time_scale), &(sds->vorticity), &(sds->border_collisions), &(sds->burning_rate), &(sds->flame_smoke), sds->flame_smoke_color, &(sds->flame_vorticity), &(sds->flame_ignition), &(sds->flame_max_temp)); + /*initializing mantaflow fields only if low-res sim + if wavelets present, init in smoke_reallocate_highres_fluid + */ /* reallocate shadow buffer */ if (sds->shadow) MEM_freeN(sds->shadow); @@ -538,7 +542,7 @@ void smokeModifier_createType(struct SmokeModifierData *smd) smd->domain->alpha = -0.001; smd->domain->beta = 0.1; smd->domain->time_scale = 1.0; - smd->domain->vorticity = 2.0; + smd->domain->vorticity = 0.01; smd->domain->border_collisions = SM_BORDER_OPEN; // open domain smd->domain->flags = MOD_SMOKE_DISSOLVE_LOG; smd->domain->highres_sampling = SM_HRES_FULLSAMPLE; @@ -563,6 +567,18 @@ void smokeModifier_createType(struct SmokeModifierData *smd) smd->domain->viewsettings = MOD_SMOKE_VIEW_SHOWBIG; smd->domain->effector_weights = BKE_add_effector_weights(NULL); + + /*mantaflow settings*/ + smd-> domain->manta_solver_res = 3; + smd->domain->manta_sim_frame = -1; + smd-> domain->manta_start_frame = 1; + smd-> domain->manta_end_frame = 10; + smd->domain->noise_clamp_neg = 0; + smd->domain->noise_clamp_pos = 1; + smd->domain->noise_val_scale = 0.; + smd->domain->noise_val_offset = 0.075; + smd->domain->noise_time_anim = 0.2; + BLI_make_file_string("/", smd->domain->_manta_filepath, BLI_temp_dir_base(), "manta_scene.py"); } else if (smd->type & MOD_SMOKE_TYPE_FLOW) { @@ -852,6 +868,7 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, unsigned int collIndex; unsigned char *obstacles = smoke_get_obstacle(sds->fluid); + int *manta_obstacles = smoke_get_manta_flags(sds->fluid); float *velx = NULL; float *vely = NULL; float *velz = NULL; @@ -901,20 +918,25 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, if (collobjs) MEM_freeN(collobjs); - + + float *manta_obs_sdf = MEM_callocN(sds->res[0] * sds->res[1] * sds->res[2] * sizeof(float), "manta_obstacle_SDF"); /* obstacle cells should not contain any velocity from the smoke simulation */ - for (z = 0; z < sds->res[0] * sds->res[1] * sds->res[2]; z++) + int loopLimit = (sds->manta_solver_res == 3)?sds->res[0] * sds->res[1] * sds->res[2]:sds->res[0] * sds->res[2]; + for (z = 0; z < loopLimit; z++) { + manta_obs_sdf[z] = 0.; if (obstacles[z]) { - velxOrig[z] = 0; - velyOrig[z] = 0; - velzOrig[z] = 0; + manta_obs_sdf[z] = 1.; + manta_obstacles[z] = 2;/*manta obstacle flag*/ +// velxOrig[z] = 0; +// velyOrig[z] = 0; +// velzOrig[z] = 0; density[z] = 0; - if (fuel) { - fuel[z] = 0; - flame[z] = 0; - } +// if (fuel) { +// fuel[z] = 0; +// flame[z] = 0; +// } if (r) { r[z] = 0; g[z] = 0; @@ -922,6 +944,7 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, } } } + MEM_freeN(manta_obs_sdf); } @@ -1554,6 +1577,7 @@ static void sample_derivedmesh( static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, SmokeFlowSettings *sfs, EmissionMap *em, float dt) { + clock_t start = clock(); if (sfs->dm) { DerivedMesh *dm; int defgrp_index = sfs->vgroup_density - 1; @@ -1696,6 +1720,10 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo dm->needsFree = 1; dm->release(dm); } + clock_t end = clock(); + float seconds = (float)(end - start) / CLOCKS_PER_SEC; + printf("TIME FOR RECONSTRUCTING SDF: %f \n", seconds); + int kkk = 9; } /********************************************************** @@ -2220,8 +2248,8 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd // we got nice flow object SmokeFlowSettings *sfs = smd2->flow; EmissionMap *em = &emaps[flowIndex]; - float *density = smoke_get_density(sds->fluid); + float *inflow_grid = smoke_get_inflow_grid(sds->fluid); float *color_r = smoke_get_color_r(sds->fluid); float *color_g = smoke_get_color_g(sds->fluid); float *color_b = smoke_get_color_b(sds->fluid); @@ -2246,7 +2274,11 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd int ii, jj, kk, gx, gy, gz, ex, ey, ez, dx, dy, dz, block_size; size_t e_index, d_index, index_big; - + float *manta_big_inflow_sdf; + if ((sds->flags & MOD_SMOKE_USE_MANTA) && (bigdensity)){ + smoke_turbulence_get_res(sds->wt, bigres); + manta_big_inflow_sdf = MEM_callocN(bigres[0] * bigres[1] * bigres[2] * sizeof(float), "manta_highres_inflow"); + } // loop through every emission map cell for (gx = em->min[0]; gx < em->max[0]; gx++) for (gy = em->min[1]; gy < em->max[1]; gy++) @@ -2267,12 +2299,15 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd if (dx < 0 || dy < 0 || dz < 0 || dx >= sds->res[0] || dy >= sds->res[1] || dz >= sds->res[2]) continue; if (sfs->type == MOD_SMOKE_FLOW_TYPE_OUTFLOW) { // outflow + apply_outflow_fields(d_index, inflow_grid, heat, fuel, react, color_r, color_g, color_b); apply_outflow_fields(d_index, density, heat, fuel, react, color_r, color_g, color_b); } else { // inflow apply_inflow_fields(sfs, emission_map[e_index], d_index, density, heat, fuel, react, color_r, color_g, color_b); - - /* initial velocity */ + if(sds->manta_solver_res == 3){ + apply_inflow_fields(sfs, emission_map[e_index], d_index, inflow_grid, heat, fuel, react, color_r, color_g, color_b); + } + /* initial velocity */ if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY) { velocity_x[d_index] = ADD_IF_LOWER(velocity_x[d_index], velocity_map[e_index * 3]); velocity_y[d_index] = ADD_IF_LOWER(velocity_y[d_index], velocity_map[e_index * 3 + 1]); @@ -2359,11 +2394,31 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd } else { // inflow apply_inflow_fields(sfs, interpolated_value, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b); + if(sds->flags & MOD_SMOKE_USE_MANTA){ + manta_big_inflow_sdf[index_big] = interpolated_value; + } } } // hires loop } // bigdensity } // low res loop + { /*2D solver*/ + int cnty; + int cntz; + int step; + for ( cnty=0;cnty<sds->res_max[1]; ++cnty) + for( cntz=0;cntz<sds->res_max[2]; ++cntz) + { + step = sds->res_max[0]/2 + cnty * sds->res_max[0] + cntz * sds->res_max[0]*sds->res_max[1]; + inflow_grid[cnty + cntz*sds->res_max[0]] = density[step]; + } + } + + if((sds->flags & MOD_SMOKE_USE_MANTA) && (bigdensity)){ +// manta_write_emitters(sfs,true,0,0,0,bigres[0], bigres[1], bigres[2], bigres[0], bigres[1], bigres[2],manta_big_inflow_sdf, NULL); + MEM_freeN(manta_big_inflow_sdf); + } + // free emission maps em_freeData(em); @@ -2409,10 +2464,8 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, float mag; float voxelCenter[3] = {0, 0, 0}, vel[3] = {0, 0, 0}, retvel[3] = {0, 0, 0}; unsigned int index = smoke_get_index(x, sds->res[0], y, sds->res[1], z); - if (((fuel ? MAX2(density[index], fuel[index]) : density[index]) < FLT_EPSILON) || obstacle[index]) continue; - vel[0] = velocity_x[index]; vel[1] = velocity_y[index]; vel[2] = velocity_z[index]; @@ -2448,6 +2501,74 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, pdEndEffectors(&effectors); } +void manta_update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, float UNUSED(dt)) +{ + ListBase *effectors; + /* make sure smoke flow influence is 0.0f */ + sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f; + effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true); + + if (effectors) + { + float *density = smoke_get_density(sds->fluid); + float *fuel = smoke_get_fuel(sds->fluid); + float *force_x = smoke_get_force_x(sds->fluid); + float *force_y = smoke_get_force_y(sds->fluid); + float *force_z = smoke_get_force_z(sds->fluid); + float *velocity_x = smoke_get_velocity_x(sds->fluid); + float *velocity_y = smoke_get_velocity_y(sds->fluid); + float *velocity_z = smoke_get_velocity_z(sds->fluid); + unsigned char *obstacle = smoke_get_obstacle(sds->fluid); + int x; + + // precalculate wind forces +#pragma omp parallel for schedule(static) + for (x = 0; x < sds->res[0]; x++) + { + int y, z; + for (y = 0; y < sds->res[1]; y++) + for (z = 0; z < sds->res[2]; z++) + { + EffectedPoint epoint; + float mag; + float voxelCenter[3] = {0, 0, 0}, vel[3] = {0, 0, 0}, retvel[3] = {0, 0, 0}; + unsigned int index = smoke_get_index(x, sds->res[0], y, sds->res[1], z); + + vel[0] = velocity_x[index]; + vel[1] = velocity_y[index]; + vel[2] = velocity_z[index]; + + /* convert vel to global space */ + mag = len_v3(vel); + mul_mat3_m4_v3(sds->obmat, vel); + normalize_v3(vel); + mul_v3_fl(vel, mag); + + voxelCenter[0] = sds->p0[0] + sds->cell_size[0] * ((float)(x + sds->res_min[0]) + 0.5f); + voxelCenter[1] = sds->p0[1] + sds->cell_size[1] * ((float)(y + sds->res_min[1]) + 0.5f); + voxelCenter[2] = sds->p0[2] + sds->cell_size[2] * ((float)(z + sds->res_min[2]) + 0.5f); + mul_m4_v3(sds->obmat, voxelCenter); + + pd_point_from_loc(scene, voxelCenter, vel, index, &epoint); + pdDoEffectors(effectors, NULL, sds->effector_weights, &epoint, retvel, NULL); + + /* convert retvel to local space */ + mag = len_v3(retvel); + mul_mat3_m4_v3(sds->imat, retvel); + normalize_v3(retvel); + mul_v3_fl(retvel, mag); + + // TODO dg - do in force! + force_x[index] = min_ff(max_ff(-1.0f, retvel[0] * 0.2f), 1.0f); + force_y[index] = min_ff(max_ff(-1.0f, retvel[1] * 0.2f), 1.0f); + force_z[index] = min_ff(max_ff(-1.0f, retvel[2] * 0.2f), 1.0f); + } + } + } + + pdEndEffectors(&effectors); +} + static void step(Scene *scene, Object *ob, SmokeModifierData *smd, DerivedMesh *domain_dm, float fps, bool for_render) { SmokeDomainSettings *sds = smd->domain; @@ -2523,7 +2644,8 @@ static void step(Scene *scene, Object *ob, SmokeModifierData *smd, DerivedMesh * if (sds->total_cells > 1) { update_effectors(scene, ob, sds, dtSubdiv); // DG TODO? problem --> uses forces instead of velocity, need to check how they need to be changed with variable dt - smoke_step(sds->fluid, gravity, dtSubdiv); + smoke_step(sds->fluid, gravity, dtSubdiv); + } } } @@ -2659,6 +2781,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * else if (smd->type & MOD_SMOKE_TYPE_DOMAIN) { SmokeDomainSettings *sds = smd->domain; + sds->manta_obj = ob; PointCache *cache = NULL; PTCacheID pid; int startframe, endframe, framenr; @@ -2669,6 +2792,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * //printf("time: %d\n", scene->r.cfra); cache = sds->point_cache[0]; + BKE_ptcache_id_from_smoke(&pid, ob, smd); BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, ×cale); @@ -2695,14 +2819,14 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * printf("bad smokeModifier_init\n"); return; } - + /* try to read from cache */ if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) { BKE_ptcache_validate(cache, framenr); smd->time = framenr; return; } - + /* only calculate something when we advanced a single frame */ if (framenr != (int)smd->time + 1) return; @@ -2725,8 +2849,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * // simulate the actual smoke (c++ code in intern/smoke) // DG: interesting commenting this line + deactivating loading of noise files - if (framenr != startframe) - { + if (framenr != startframe){ if (sds->flags & MOD_SMOKE_DISSOLVE) { /* low res dissolve */ smoke_dissolve(sds->fluid, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG); @@ -2734,20 +2857,16 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * if (sds->wt) { smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG); } - } - step(scene, ob, smd, dm, scene->r.frs_sec / scene->r.frs_sec_base, for_render); } - // create shadows before writing cache so they get stored smoke_calc_transparency(sds, scene); - if (sds->wt) + if (sds->wt&& !(smd->domain->flags & MOD_SMOKE_USE_MANTA)) { smoke_turbulence_step(sds->wt, sds->fluid); } - BKE_ptcache_validate(cache, framenr); if (framenr != startframe) BKE_ptcache_write(&pid, framenr); diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index b5adf38527b..d96bba4da45 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -54,6 +54,7 @@ #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_smoke.h" #include "LBM_fluidsim.h" @@ -65,6 +66,11 @@ #include "physics_intern.h" // own include /* enable/disable overall compilation */ +/*mantaflow include*/ +#include "../../../../intern/smoke/extern/smoke_API.h" +//#include "../../blenkernel/intern/smoke.c" +#include "DNA_smoke_types.h" + #ifdef WITH_MOD_FLUID #include "WM_api.h" @@ -72,7 +78,6 @@ #include "DNA_scene_types.h" #include "DNA_mesh_types.h" - static float get_fluid_viscosity(FluidsimSettings *settings) { return (1.0f/powf(10.0f, settings->viscosityExponent)) * settings->viscosityValue; @@ -1127,3 +1132,129 @@ void FLUID_OT_bake(wmOperatorType *ot) ot->poll = ED_operator_object_active_editable; } + + +static int manta_make_file_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + +static int manta_make_file_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + + if (smd->domain->fluid == NULL) + { + smoke_reallocate_fluid(smd->domain, smd->domain->dx, smd->domain->res, 1); + if (smd->domain->flags & MOD_SMOKE_HIGHRES) { + smoke_reallocate_highres_fluid(smd->domain, smd->domain->dx, smd->domain->res, 1); + } + } + if (smd->domain->fluid) + { + smoke_manta_export(smd); + } + + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + + +void MANTA_OT_make_file(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Mantaflow File"; + ot->description = "Create Python Script for Simulation"; + ot->idname = "MANTA_OT_make_file"; + + /* api callbacks */ + ot->invoke = manta_make_file_invoke; + ot->exec = manta_make_file_exec; + ot->poll = ED_operator_object_active_editable; +} + +static int manta_sim_step_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + +static int manta_sim_step_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + + +void MANTA_OT_sim_step(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Run Mantaflow Step"; + ot->description = "Run One Step of Mantaflow Simulation"; + ot->idname = "MANTA_OT_sim_step"; + + /* api callbacks */ + ot->invoke = manta_sim_step_invoke; + ot->exec = manta_sim_step_exec; + ot->poll = ED_operator_object_active_editable; +} + +static int manta_stop_sim_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ +// Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + smoke_mantaflow_stop_sim(smd->domain->fluid); + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + +static int manta_stop_sim_exec(bContext *C, wmOperator *op) +{ +// Scene *scene= CTX_data_scene(C); + SmokeModifierData *smd; + Object * smokeDomain = CTX_data_active_object(C); + smd = (SmokeModifierData *)modifiers_findByType(smokeDomain, eModifierType_Smoke); + smoke_mantaflow_stop_sim(smd->domain->fluid); + + /* return OPERATOR_CANCELLED;*/ + + return OPERATOR_FINISHED; +} + + +void MANTA_OT_stop_sim(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stop Mantaflow Sim"; + ot->description = "Stop Mantaflow Sim"; + ot->idname = "MANTA_OT_stop_sim"; + + /* api callbacks */ + ot->invoke = manta_stop_sim_invoke; + ot->exec = manta_stop_sim_exec; + ot->poll = ED_operator_object_active_editable; +} diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 666ed2397d2..3bb87b80333 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -92,7 +92,9 @@ void BOID_OT_state_move_down(struct wmOperatorType *ot); /* physics_fluid.c */ void FLUID_OT_bake(struct wmOperatorType *ot); - +void MANTA_OT_make_file(struct wmOperatorType *ot); +void MANTA_OT_sim_step(struct wmOperatorType *ot); +void MANTA_OT_stop_sim(struct wmOperatorType *ot); /* dynamicpaint.c */ void DPAINT_OT_bake(struct wmOperatorType *ot); void DPAINT_OT_surface_slot_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index c765bff796e..cc87e61ff61 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -178,6 +178,16 @@ static void operatortypes_fluid(void) WM_operatortype_append(FLUID_OT_bake); } +/********************************* manta ***********************************/ + +static void operatortypes_manta(void) +{ + WM_operatortype_append(MANTA_OT_make_file); + WM_operatortype_append(MANTA_OT_sim_step); + WM_operatortype_append(MANTA_OT_stop_sim); +} + + /**************************** point cache **********************************/ static void operatortypes_pointcache(void) @@ -219,6 +229,7 @@ void ED_operatortypes_physics(void) operatortypes_particle(); operatortypes_boids(); operatortypes_fluid(); + operatortypes_manta(); operatortypes_pointcache(); operatortypes_dynamicpaint(); } diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h index 25c98b4f07e..f3a88f94029 100644 --- a/source/blender/makesdna/DNA_smoke_types.h +++ b/source/blender/makesdna/DNA_smoke_types.h @@ -37,10 +37,13 @@ enum { MOD_SMOKE_HIGHRES = (1 << 1), /* enable high resolution */ MOD_SMOKE_DISSOLVE = (1 << 2), /* let smoke dissolve */ MOD_SMOKE_DISSOLVE_LOG = (1 << 3), /* using 1/x for dissolve */ + MOD_SMOKE_USE_MANTA = (1 << 4), MOD_SMOKE_HIGH_SMOOTH = (1 << 5), /* -- Deprecated -- */ MOD_SMOKE_FILE_LOAD = (1 << 6), /* flag for file load */ MOD_SMOKE_ADAPTIVE_DOMAIN = (1 << 7), + MOD_SMOKE_NOISE_CLAMP = (1 << 8), /*for mantaflow*/ + MOD_SMOKE_MANTA_USE_LIQUID = (1 << 9),/*enable liquid simulation*/ }; #if (DNA_DEPRECATED_GCC_POISON == 1) @@ -79,6 +82,14 @@ enum { #define SM_ACTIVE_COLORS (1<<2) #define SM_ACTIVE_COLOR_SET (1<<3) +/*Container for all smoke solvers: blender and manta*/ +typedef struct SmokeSolvers{ + int type;/*which solver is currently active*/ + int mock_var; + struct FLUID_3D *fluid_blender; + +}SmokeSolvers; + typedef struct SmokeDomainSettings { struct SmokeModifierData *smd; /* for fast RNA access */ struct FLUID_3D *fluid; @@ -91,6 +102,7 @@ typedef struct SmokeDomainSettings { struct GPUTexture *tex_wt; struct GPUTexture *tex_shadow; struct GPUTexture *tex_flame; + struct Object *manta_obj; float *shadow; /* simulation data */ @@ -106,6 +118,8 @@ typedef struct SmokeDomainSettings { float imat[4][4]; /* domain object imat */ float obmat[4][4]; /* domain obmat */ + char _manta_filepath[1024]; /* FILE_MAX */ + int base_res[3]; /* initial "non-adapted" resolution */ int res_min[3]; /* cell min */ int res_max[3]; /* cell max */ @@ -149,9 +163,22 @@ typedef struct SmokeDomainSettings { float burning_rate, flame_smoke, flame_vorticity; float flame_ignition, flame_max_temp; float flame_smoke_color[3]; + /* mantaflow settings */ + int manta_solver_res; /*dimension of manta solver, 2d or 3d*/ + int manta_start_frame; + int manta_end_frame; + int manta_uvs_num; /*number of UVs, important for octaves count*/ + /*noise settings*/ + float noise_clamp_neg; + float noise_clamp_pos; + float noise_val_scale; + float noise_val_offset; + float noise_time_anim; + int manta_sim_frame;/*current simulation frame number. If not simulating-> manta_sim_frame == -1*/ + float mock_var; /*not used*/ +// float mock_var2; /*not used*/ } SmokeDomainSettings; - /* inflow / outflow */ /* type */ diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index fa7fd6da8bb..fa94e345e3f 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -254,6 +254,10 @@ if(WITH_MOD_SMOKE) add_definitions(-DWITH_SMOKE) endif() +if(WITH_MOD_MANTA) + add_definitions(-DWITH_MANTA) +endif() + if(WITH_MOD_OCEANSIM) add_definitions(-DWITH_OCEANSIM) endif() diff --git a/source/blender/makesrna/intern/SConscript b/source/blender/makesrna/intern/SConscript index 3d190fc6a13..e3dbab0ffa7 100644 --- a/source/blender/makesrna/intern/SConscript +++ b/source/blender/makesrna/intern/SConscript @@ -62,6 +62,7 @@ incs = [ '#/intern/audaspace/intern', '#/intern/cycles/blender', '#/intern/smoke/extern', + '#/source//blender/python/manta_pp', '../../blenfont', '../../blenkernel', '../../blenlib', @@ -81,6 +82,9 @@ incs = ' '.join(incs) if env['WITH_BF_SMOKE']: defs.append('WITH_SMOKE') +if env['WITH_BF_MANTA']: + defs.append('WITH_MANTA') + if env['WITH_BF_BULLET']: defs.append('WITH_BULLET') incs += ' #/intern/rigidbody' diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c index 55262a98b2e..221b311aa9e 100644 --- a/source/blender/makesrna/intern/rna_smoke.c +++ b/source/blender/makesrna/intern/rna_smoke.c @@ -97,6 +97,26 @@ static void rna_Smoke_reset_dependency(Main *bmain, Scene *scene, PointerRNA *pt rna_Smoke_dependency_update(bmain, scene, ptr); } +static void rna_Smoke_manta_write_settings(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data; + rna_Smoke_reset(bmain,scene,ptr); +} + +static void rna_Smoke_manta_switch2D(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data; + if (settings->manta_solver_res == 2) + { + settings->base_res[1] = 1; + } + else if(settings->manta_solver_res == 3) + { + settings->base_res[1] = 5; + } + rna_Smoke_reset(bmain,scene,ptr); +} + static char *rna_SmokeDomainSettings_path(PointerRNA *ptr) { SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data; @@ -544,6 +564,99 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Threshold", "Maximum amount of fluid cell can contain before it is considered empty"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + /* mantaflow variables */ + prop = RNA_def_property(srna, "use_manta", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_USE_MANTA); + RNA_def_property_ui_text(prop, "MantaFlow", "Use Mantaflow"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_manta_write_settings"); + + prop = RNA_def_property(srna, "manta_filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "_manta_filepath"); + RNA_def_property_ui_text(prop, "Output Path", + "Directory/name to save Mantaflow scene for further simulations"); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_manta_write_settings"); + + prop = RNA_def_property(srna, "use_manta_liquid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_MANTA_USE_LIQUID); + RNA_def_property_ui_text(prop, "MantaFlow Liquid", "Use Mantaflow liquid"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset"); + + prop = RNA_def_property(srna, "manta_solver_res", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "manta_solver_res"); + RNA_def_property_range(prop, 2, 3); + RNA_def_property_ui_range(prop, 2, 3, 1, -1); + RNA_def_property_ui_text(prop, "Solver Res", "Solver resolution(2D/3D)"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_manta_switch2D"); + + prop = RNA_def_property(srna, "manta_sim_frame", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "manta_sim_frame"); + RNA_def_property_range(prop, -1, 250); + RNA_def_property_ui_range(prop, -1, 250, 1, -1); + RNA_def_property_ui_text(prop, "Current Simulation Frame", "-1, if not simulating"); + + prop = RNA_def_property(srna, "manta_start_frame", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "manta_start_frame"); + RNA_def_property_range(prop, 0, 249); + RNA_def_property_ui_range(prop, 0, 249, 1, -1); + RNA_def_property_ui_text(prop, "Sim Start", "Frame from which to start simulation"); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "manta_end_frame", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "manta_end_frame"); + RNA_def_property_range(prop, 1, 250); + RNA_def_property_ui_range(prop, 1, 250, 1, -1); + RNA_def_property_ui_text(prop, "Sim End", "Frame on which to end simulation"); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "manta_uvs", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "manta_uvs_num"); + RNA_def_property_range(prop, 0, 4); + RNA_def_property_ui_range(prop, 0, 4, 1, -1); + RNA_def_property_ui_text(prop, "UVs number", "Number of UV coordinate grids to use(Better not more than 2)"); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset"); + + prop = RNA_def_property(srna, "manta_clamp_noise", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_NOISE_CLAMP); + RNA_def_property_ui_text(prop, "MantaFlow", "Use Mantaflow"); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "noise_clamp_neg", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_clamp_neg"); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5); + RNA_def_property_ui_text(prop, "Clamp Negative Noise", ""); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "noise_clamp_pos", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_clamp_pos"); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5); + RNA_def_property_ui_text(prop, "Clamp Positive Noise", ""); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "noise_val_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_val_scale"); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5); + RNA_def_property_ui_text(prop, "Noise Value Scale", ""); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "noise_val_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_val_offset"); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5); + RNA_def_property_ui_text(prop, "Noise Value Offset", ""); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + prop = RNA_def_property(srna, "noise_time_anim", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_time_anim"); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5); + RNA_def_property_ui_text(prop, "Noise animation time", ""); +// RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + + } static void rna_def_smoke_flow_settings(BlenderRNA *brna) diff --git a/source/blender/python/CMakeLists.txt b/source/blender/python/CMakeLists.txt index e855f3a3756..550e791425c 100644 --- a/source/blender/python/CMakeLists.txt +++ b/source/blender/python/CMakeLists.txt @@ -20,3 +20,5 @@ add_subdirectory(intern) add_subdirectory(generic) add_subdirectory(mathutils) add_subdirectory(bmesh) +#choose "manta_pp" for preprocessed files or "manta_full" for full manta module +add_subdirectory(manta_pp) diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript index 5f032f25cf3..3b46b3ff917 100644 --- a/source/blender/python/SConscript +++ b/source/blender/python/SConscript @@ -78,6 +78,12 @@ defs = [] sources = env.Glob('mathutils/*.c') env.BlenderLib( libname = 'bf_python_mathutils', sources = Split(sources), includes = Split(incs), defines = defs, libtype = ['core','player'], priority = [362,165]) +# manta +defs = [] + +sources = env.Glob('manta_pp/source/*') +env.BlenderLib( libname = 'bf_python_manta', sources = Split(sources), includes = Split(incs), defines = defs, libtype = ['core','player']) + # bpy defs = env['BF_GL_DEFINITIONS'] @@ -186,6 +192,9 @@ if env['WITH_BF_REMESH']: if env['WITH_BF_SMOKE']: defs.append('WITH_SMOKE') +if env['WITH_BF_MANTA']: + defs.append('WITH_MANTA') + if env['WITH_BF_COLLADA']: defs.append('WITH_COLLADA') diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 8296027f044..42112d41ea6 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -253,6 +253,10 @@ if(WITH_MOD_SMOKE) add_definitions(-DWITH_SMOKE) endif() +if(WITH_MOD_MANTA) + add_definitions(-DWITH_MANTA) +endif() + if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 5b4db89a41a..f0c0e2c5fe8 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -83,6 +83,8 @@ #include "../bmesh/bmesh_py_api.h" #include "../mathutils/mathutils.h" +#include "../manta_pp/pwrapper/manta_api.h" + /* for internal use, when starting and ending python scripts */ @@ -223,6 +225,7 @@ static struct _inittab bpy_internal_modules[] = { {"bgl", BPyInit_bgl}, {"blf", BPyInit_blf}, {"bmesh", BPyInit_bmesh}, + { "manta", PyInit_Manta}, #if 0 {"bmesh.types", BPyInit_bmesh_types}, {"bmesh.utils", BPyInit_bmesh_utils}, @@ -275,7 +278,7 @@ void BPY_python_start(int argc, const char **argv) #endif Py_FrozenFlag = 1; - +// PyImport_AppendInittab("manta", PyInit_Manta); Py_Initialize(); // PySys_SetArgv(argc, argv); /* broken in py3, not a huge deal */ diff --git a/source/blender/python/manta_full/CMakeLists.txt b/source/blender/python/manta_full/CMakeLists.txt new file mode 100755 index 00000000000..d04c70bae72 --- /dev/null +++ b/source/blender/python/manta_full/CMakeLists.txt @@ -0,0 +1,597 @@ +#****************************************************************************** +# +# MantaFlow fluid solver framework +# +# Copyright 2011-2014 Tobias Pfaff, Nils Thuerey +# +# This program is free software, distributed under the terms of the +# GNU General Public License (GPL) +# http://www.gnu.org/licenses +# +#****************************************************************************** + +#project (MantaFlow) +cmake_minimum_required(VERSION 2.8) # 2.8 needed for CUDA + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/source/cmake/") +SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) +SET(VERBOSE 1) + +SET(MANTAVERSION "0.6") +#****************************************************************************** +# Default paths +# - CMake's path finder is completely useless for Qt5 + Python on Win64 + +IF(WIN32) + set(WIN_QT_PATH "C:/Qt/Qt5.2.1/5.2.1/msvc2012_64_opengl") # qt5/win64 + # set(WIN_QT_PATH "C:/Qt/4.8.6") # qt4/win32 + set(WIN_PYTHON_PATH "C:/Python34") + set(WIN_PYTHON_VERSION "34") + set(CMAKE_LIBRARY_PATH "C:/Program Files (x86)/Windows Kits/8.0/Lib/win8/um/x64") + SET(CMAKE_PREFIX_PATH ${WIN_QT_PATH}) +ENDIF() + +IF(APPLE) + SET(CMAKE_PREFIX_PATH "/usr/local/Cellar/qt5/5.2.1/") # mac/homebrew +ENDIF() + +#****************************************************************************** +# setup default params + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Debug") +ELSE() + MESSAGE("Build-type: '${CMAKE_BUILD_TYPE}'") +ENDIF() + +# compilation versions +OPTION(DEBUG "Enable debug compilation" OFF) +OPTION(GUI "Compile with GUI (requires QT)" OFF) +OPTION(TBB "Use multi-thread kernels using Intels TBB" OFF) +OPTION(OPENMP "Use multi-thread kernels using OpenMP" OFF) +OPTION(CUDA "Compile with CUDA plugins" OFF) +OPTION(PREPDEBUG "Debug generated files" OFF) # This will beautify generated files, and link to them for compiler errors instead of the original sources +OPTION(DOUBLEPRECISION "Compile with double floating point precision" OFF) + +#check consistency of MT options +set(MT OFF) +set(MT_TYPE "NONE") +if (TBB) + set (MT_TYPE "TBB") + set (MT ON) +endif() +if (OPENMP) + set (MT_TYPE "OPENMP") + set (MT ON) +endif() +if (TBB AND OPENMP) + message(FATAL_ERROR "Cannot activate both OPENMP and TBB") +endif() + +# make sure debug settings match... +IF(NOT DEBUG) + IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + SET(DEBUG 1) + ENDIF() +ENDIF() +IF(DEBUG) + SET(CMAKE_BUILD_TYPE "Debug") + add_definitions ( -DDEBUG ) +ENDIF() + +MESSAGE(STATUS "") +MESSAGE(STATUS "Options - " + " -DDEBUG='${DEBUG}' " + " -DGUI='${GUI}' " + " -DTBB='${TBB}' " + " -DOPENMP='${OPENMP}' " + " -DPREPDEBUG='${PREPDEBUG}' " + " -DCUDA='${CUDA}' " + " -DDOUBLEPRECISION='${DOUBLEPRECISION}' " + ) +MESSAGE(STATUS "Multithreading type : ${MT_TYPE}") +MESSAGE(STATUS "") + +#****************************************************************************** +# Pre-processor + +# compile prep +SET(SOURCES + source/preprocessor/main.cpp + source/preprocessor/code.cpp + source/preprocessor/tokenize.cpp + source/preprocessor/parse.cpp + source/preprocessor/util.cpp + source/preprocessor/merge.cpp + source/preprocessor/codegen_python.cpp + source/preprocessor/codegen_kernel.cpp +) +add_executable(prep ${SOURCES}) +if (NOT WIN32) +#PR commented +# set_target_properties(prep PROPERTIES COMPILE_FLAGS "-Wall -O2") +endif() + +#****************************************************************************** +# Setup main project + +SET(F_LIBS "" ) +SET(F_LIB_PATHS) +SET(F_LINKADD "") # additional linker flags, not a list +set(PP_PATH "pp") +SET(SILENT_SOURCES) + +# need pre-processing +SET(PP_SOURCES + source/fluidsolver.cpp + source/conjugategrad.cpp + source/grid.cpp + source/levelset.cpp + source/fastmarch.cpp + source/shapes.cpp + source/mesh.cpp + source/general.cpp + source/particle.cpp + source/flip.cpp + source/movingobs.cpp + source/fileio.cpp + source/noisefield.cpp + source/kernel.cpp + source/vortexsheet.cpp + #source/vortexfilament.cpp + source/vortexpart.cpp + source/turbulencepart.cpp + source/edgecollapse.cpp + + source/plugin/pressure.cpp + source/plugin/initplugins.cpp + #PR changed + source/plugin/advection.cpp + source/plugin/extforces.cpp + source/plugin/kepsilon.cpp + source/plugin/meshplugins.cpp + source/plugin/vortexplugins.cpp + source/plugin/waveletturbulence.cpp + source/python/defines.py + source/test.cpp +) + +SET(PP_HEADERS + source/commonkernels.h + source/conjugategrad.h + source/fastmarch.h + source/fluidsolver.h + source/grid.h + source/mesh.h + source/particle.h + source/levelset.h + source/shapes.h + source/noisefield.h + source/vortexsheet.h + #source/vortexfilament.h + source/general.h + source/kernel.h + source/flip.h + source/movingobs.h + source/fileio.h + source/edgecollapse.h + source/vortexpart.h + source/turbulencepart.h + #source/test.h + #PR changed +# source/advection.h +) + +# no pre-processing needed +set(NOPP_SOURCES + source/pwrapper/pymain.cpp + source/pwrapper/pclass.cpp + source/pwrapper/pvec3.cpp + source/pwrapper/pconvert.cpp + source/pwrapper/registry.cpp + source/util/vectorbase.cpp + source/pwrapper/manta_api.cpp +) + +SET(NOPP_HEADERS + source/pwrapper/pythonInclude.h + source/pwrapper/pclass.h + source/pwrapper/registry.h + source/pwrapper/pconvert.h + source/util/integrator.h + source/util/vectorbase.h + source/util/quaternion.h + source/util/interpol.h + source/util/mcubes.h + source/util/randomstream.h + source/util/solvana.h + source/pwrapper/manta_api.h +) + +if (GUI) + # need QT preprocessor + set(QT_HEADERS + source/gui/mainwindow.h + source/gui/glwidget.h + source/gui/painter.h + source/gui/meshpainter.h + source/gui/qtmain.h + source/gui/customctrl.h + source/gui/particlepainter.h + ) + set(QT_SOURCES + source/gui/customctrl.cpp + source/gui/mainwindow.cpp + source/gui/glwidget.cpp + source/gui/customctrl.cpp + source/gui/painter.cpp + source/gui/meshpainter.cpp + source/gui/particlepainter.cpp + source/gui/qtmain.cpp + ) + list(APPEND PP_SOURCES ${QT_SOURCES}) + list(APPEND PP_HEADERS ${QT_HEADERS}) +endif() + +# CUDA sources +if (CUDA) + list(APPEND PP_SOURCES + source/cuda/meshtools.cu + source/cuda/buoyancy.cu + source/cuda/particle.cu + ) +endif() + +# include dirs +message("PRincluding _ ${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source") +SET(INCLUDE_PATHS + source/pwrapper + source/cuda + source/util + ${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source + #PR added + ${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source/plugin +) + +# Multithreading +if(MT) + add_definitions( -DMT) + if(TBB) + # Intel TBB + add_definitions( -DTBB) + + if (DEBUG) + add_definitions( -DTBB_USE_DEBUG=1 ) + list(APPEND F_LIBS tbb) + else() + list(APPEND F_LIBS tbb) + endif() + if (WIN32) + find_package(TBB REQUIRED) + list(APPEND INCLUDE_PATHS ${TBB_INCLUDE_DIRS}) + list(APPEND F_LIB_PATHS ${TBB_LIBRARY_DIRS}) + elseif(APPLE) + find_package(TBB REQUIRED) + list(APPEND INCLUDE_PATHS ${TBB_INCLUDE_DIRS}) + list(APPEND F_LIB_PATHS ${TBB_LIBRARY_DIRS}) + endif() + else() + # OpenMP + add_definitions( -DOPENMP ) + + if (WIN32) + add_definitions( /openmp) + else() + add_definitions(-fopenmp) + SET(F_LINKADD "${F_LINKADD} -fopenmp ") + endif() + endif() +endif() + +#****************************************************************************** +# add a target to generate API documentation with Doxygen + +find_package(Doxygen) +if(DOXYGEN_FOUND) + set(DX_PATH "doxy") + foreach(it ${PP_SOURCES} ${PP_HEADERS} ${NOPP_SOURCES} ${NOPP_HEADERS}) + get_filename_component(CURPATH ${it} PATH) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${DX_PATH}/${CURPATH}") + set(CURDX "${DX_PATH}/${it}") + string(REPLACE "/" "_" TGT ${CURDX}) + string(REPLACE "source/" "" INFILE ${it}) + add_custom_command(OUTPUT ${TGT} + COMMAND prep docgen "0" ${MT_TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/source/" "${INFILE}" "${CURDX}" + DEPENDS prep + IMPLICIT_DEPENDS CXX ${it} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + list(APPEND TGLIST ${TGT}) + endforeach(it) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${TGLIST} + COMMENT "Generating API documentation with Doxygen" VERBATIM + ) +endif(DOXYGEN_FOUND) + + + +#****************************************************************************** +# Link libraries + +# Python +#find_package(PythonLibs QUIET) +#if((NOT PYTHONLIBS_FOUND) AND WIN32) +# set(PYTHON_INCLUDE_DIR "${WIN_PYTHON_PATH}/include") +# set(PYTHON_LIBRARY "${WIN_PYTHON_PATH}/libs/python${WIN_PYTHON_VERSION}.lib") +#endif() +#find_package(PythonLibs REQUIRED) +#list(APPEND INCLUDE_PATHS ${PYTHON_INCLUDE_DIRS}) +#list(APPEND F_LIBS ${PYTHON_LIBRARIES}) + +#PR PYTHON +set(PYTHON_VERSION 3.4) +set(PYTHON_INCLUDE_DIR "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}/include/python${PYTHON_VERSION}m") +set(PYTHON_BINARY "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}/bin/python${PYTHON_VERSION}") +#set(PYTHON_LIBRARY python${PYTHON_VERSION}) +set(PYTHON_LIBPATH "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}/lib/python${PYTHON_VERSION}/config-${PYTHON_VERSION}m") +#set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled +find_package(PythonLibs REQUIRED) +list(APPEND INCLUDE_PATHS ${PYTHON_INCLUDE_DIR}) +list(APPEND F_LIBS ${PYTHON_LIBRARIES}) + +# Z compression +if (1) + # default: build from source + set(ZLIB_SRC adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c + inflate.c infback.c inftrees.c inffast.c trees.c uncompr.c zutil.c) + foreach(it ${ZLIB_SRC}) + list(APPEND SILENT_SOURCES dependencies/zlib-1.2.8/${it}) + + set(ZLIB_ADDFLAGS "") + if(NOT WIN32) + set(ZLIB_ADDFLAGS "-Wno-implicit-function-declaration") + endif() + endforeach(it) + set_source_files_properties(${SILENT_SOURCES} PROPERTIES COMPILE_FLAGS "${ZLIB_ADDFLAGS}") + + list(APPEND INCLUDE_PATHS dependencies/zlib-1.2.8) +else() + include(FindZLIB) + list(APPEND INCLUDE_PATHS ${ZLIB_INCLUDE_DIR}) + list(APPEND F_LIBS ${ZLIB_LIBRARIES}) +endif() + +# CUDA +if(CUDA) + add_definitions( -DCUDA=1 ) + find_package(CUDA QUIET REQUIRED) + set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF) + # ? if (USE64) set(CUDA_64_BIT_DEVICE_CODE ON) endif() + if(WIN32) + if(DEBUG) + set(CUDA_NVCC_FLAGS -DDEBUG;--compiler-options;-Wall) + else() + set(CUDA_NVCC_FLAGS --use_fast_math;-DNDEBUG;--compiler-options;-Wall;-O2) + endif() + else() + # CUDA does not support gcc > 4.5 yet + if(DEBUG) + set(CUDA_NVCC_FLAGS --pre-include;/usr/local/include/undef_atomics_int128.h;-g;-DDEBUG;-keep;--maxrregcount=31;--compiler-options;-Wall) + else() + #set(CUDA_NVCC_FLAGS -ccbin;gcc-4.5;--use_fast_math;-arch=sm_20;-DNDEBUG;--compiler-options;-Wall;-O3) + set(CUDA_NVCC_FLAGS --pre-include;/usr/local/include/undef_atomics_int128.h;--use_fast_math;-DNDEBUG;--compiler-options;-Wall;-O3) + endif() + endif() +endif() + +# increase FP precision? +if(DOUBLEPRECISION) + add_definitions(-DFLOATINGPOINT_PRECISION=2) +endif() + + +#****************************************************************************** +# generate repository info + +# TEST! NT_DEBUG +set(HGINFO "${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source/hginfo.h") +MESSAGE(STATUS "Hg info target header ${HGINFO}") +add_custom_command(OUTPUT ${HGINFO} + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tools/makeHgVersion.py" "${HGINFO}" + DEPENDS ${PP_SOURCES} ${PP_HEADERS} ${NOPP_SOURCES} ${NOPP_HEADERS} ${QT_SOURCES} ${QT_HEADERS} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) + +#****************************************************************************** +# apply preprocessor + +set(SOURCES ${NOPP_SOURCES} ${SILENT_SOURCES}) +set(HEADERS ${NOPP_HEADERS}) +set(PP_REGCPP) +set(PP_REGS) +set(PP_PREPD "0") +MESSAGE("PRPRPRPRPR_prepdebug") + +if (PREPDEBUG) + set(PP_PREPD "1") +endif() +#MESSAGE("PR_prepdebug_1 BEFORE ${SOURCES}") +foreach(it ${PP_HEADERS} ${PP_SOURCES}) + get_filename_component(CURPATH ${it} PATH) + get_filename_component(CUREXT ${it} EXT) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/${CURPATH}") + set(CURPP "${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/${it}") + string(REPLACE "source/" "" INFILE ${it}) + # preprocessor ${CMAKE_BINARY_DIR}/bin/ + add_custom_command(OUTPUT ${CURPP} + COMMAND echo Workingdir + COMMAND pwd + COMMAND prep generate ${PP_PREPD} ${MT_TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/source/" "${INFILE}" "${CURPP}" + DEPENDS prep + IMPLICIT_DEPENDS CXX ${it} +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + if ("${CUREXT}" STREQUAL ".h" OR "${CUREXT}" STREQUAL ".py") + list(APPEND PP_REGS "${CURPP}.reg") + list(APPEND PP_REGCPP "${CURPP}.reg.cpp") + set_source_files_properties("${CURPP}.reg.cpp" OBJECT_DEPENDS "${CURPP}") + endif() + list(APPEND SOURCES ${CURPP}) +endforeach(it) + + +# link reg files +add_custom_command(OUTPUT ${PP_REGCPP} + COMMAND prep link ${PP_REGS} + DEPENDS prep ${PP_REGS} +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Linking reg files") +set_source_files_properties(${PP_REGS} ${PP_REGCPP} PROPERTIES GENERATED 1) +MESSAGE("PR_prepdebug2") + +list(APPEND SOURCES ${PP_REGCPP}) +#MESSAGE("\nPR_prepdebug_1 AFTER REGCPP ${SOURCES}") + +#add_custom_target(run ALL +# DEPENDS SOURCES) + +MESSAGE("PR_prepdebug3") + +#****************************************************************************** +#PR finding what is so special about GUI +#list(APPEND INCLUDE_PATHS ${CMAKE_CURRENT_BINARY_DIR} +# ${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source/gui source/gui) +#SET(F_LINKADD "${F_LINKADD} -framework OpenGL ") + +#****************************************************************************** +# QT for GUI + +if(GUI) + # remap + set(QT_REMAP) + foreach(it ${QT_HEADERS}) + list(APPEND QT_REMAP "${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/${it}") + endforeach(it) + add_definitions(-DGUI=1) + list(APPEND INCLUDE_PATHS ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/${PP_PATH}/source/gui source/gui) + + cmake_policy(SET CMP0020 NEW) + find_package(Qt5Core QUIET) + if (Qt5Core_FOUND) + message("Using Qt5") + find_package(Qt5Widgets REQUIRED) + find_package(Qt5OpenGL REQUIRED) + + qt5_wrap_cpp(MOC_OUTFILES ${QT_REMAP} ) + qt5_add_resources(QT_RES resources/res.qrc ) + add_definitions(-DGUI=1) + add_definitions(${Qt5Widgets_DEFINITIONS}) + list(APPEND INCLUDE_PATHS ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS}) + list(APPEND F_LIBS ${Qt5Widgets_LIBRARIES} ${Qt5OpenGL_LIBRARIES}) + list(APPEND SOURCES ${MOC_OUTFILES} ${QT_RES}) + else() + message("Using Qt4") +# set(QT_QMAKE_EXECUTABLE /usr/bin/qmake-4.8) + find_package(Qt4 REQUIRED) + set(QT_USE_QTOPENGL TRUE) + + qt4_wrap_cpp(MOC_OUTFILES ${QT_REMAP} ) + qt4_add_resources(QT_RES resources/res.qrc ) + include(${QT_USE_FILE}) + add_definitions(${QT_DEFINITIONS}) + list(APPEND F_LIBS ${QT_LIBRARIES}) + list(APPEND SOURCES ${MOC_OUTFILES} ${QT_RES}) + endif() + + if (APPLE) + # mac opengl framework + SET(F_LINKADD "${F_LINKADD} -framework OpenGL ") + else() + find_package(OpenGL REQUIRED) + list(APPEND F_LIBS ${OPENGL_LIBRARIES}) + endif() +endif() + +#****************************************************************************** +# setup executable +#MESSAGE("\nSETTINGS EXEC ${SOURCES}") + +# compiler flags +IF(NOT WIN32) + IF(DEBUG) + add_definitions( -DDEBUG ) + add_definitions( -O0 -fno-inline ) + # additional c++ warning + SET(CMAKE_CXX_FLAGS_DEBUG " -ggdb -Wall") + + # stricter: no optimizations and inlining, comment out if needed... + #SET(CMAKE_CXX_FLAGS_DEBUG " -O0 -fno-inline ${CMAKE_CXX_FLAGS_DEBUG} ") + ELSE() + # non-debug, optimized version + add_definitions( -O3 -Wall) + ENDIF() +ELSE() + # get rid of some MSVC warnings + add_definitions( /wd4018 /wd4146 /wd4800 ) + + # for zlib + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) + add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) + + # unsigned to signed conversions + add_definitions( /wd4267 ) + # double <> single precision + add_definitions( /wd4244 /wd4305 ) + # disable warnings for unsecure functions + add_definitions( /D "_CRT_SECURE_NO_WARNINGS" ) +ENDIF() +MESSAGE("PR_11") +#SET(EXECCMD manta) +SET(EXECCMD bf_python_manta) +MESSAGE("PR_12") + +SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${F_LINKADD} ") +SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${F_LINKADD} ") +MESSAGE("PR_13 including parths: ${INCLUDE_PATHS}") + +include_directories( ${INCLUDE_PATHS}) + +link_directories( ${F_LIB_PATHS} ) +MESSAGE("PR_14") +#list(REMOVE_DUPLICATES SOURCES) +if(CUDA) + #cuda_include_directories(pp/source/cuda) + cuda_add_executable( ${EXECCMD} ${SOURCES} ${PP_REGISTRY}) + target_link_libraries( ${EXECCMD} ${F_LIBS} ) + cuda_build_clean_target() +else() + if (WIN32) + # make nice folders for Visual Studio + set_source_files_properties(${PP_SOURCES} ${PP_HEADERS} ${NOPP_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE) +# add_executable(${EXECCMD} ${SOURCES} ${PP_REGISTRY} ${PP_SOURCES} ${PP_HEADERS}) + blender_add_lib(${EXECCMD} "${SOURCES}" "${INCLUDE_PATHS}" "${INC_SYS}") + source_group(Generated FILES ${SOURCES} ${PP_REGISTRY} ${HEADERS}) + else() + # add_executable(${EXECCMD} ${SOURCES} ${PP_REGISTRY} ${HGINFO}) +# list(SORT SOURCES) + list(REMOVE_DUPLICATES SOURCES) #CMAKE GUI says there are duplicates, though there are none + blender_add_lib(${EXECCMD} "${SOURCES}" "${INCLUDE_PATHS}" "${INC_SYS}") + endif() + target_link_libraries( ${EXECCMD} ${F_LIBS} ) +endif() +MESSAGE("PR_15") + + +if(WIN32) + # add no-build target to create nice MSVC folder + add_custom_target(SourceFiles SOURCES ${PP_SOURCES} ${PP_HEADERS} ${QT_SOURCES} ${QT_HEADERS} ${NOPP_HEADERS} ${NOPP_SOURCES}) + source_group(Headers FILES ${PP_HEADERS} ${NOPP_HEADERS} ${QT_HEADERS}) + source_group(Sources FILES ${PP_SOURCES} ${NOPP_SOURCES} ${QT_SOURCES}) +endif() + +# debug +MESSAGE(STATUS "DEBUG Flag-Summary - Includes: '${INCLUDE_PATHS}' | Libs: '${F_LIBS}' | LibPaths: '${F_LIB_PATHS}' ")
\ No newline at end of file diff --git a/source/blender/python/manta_full/COPYING b/source/blender/python/manta_full/COPYING new file mode 100644 index 00000000000..94a9ed024d3 --- /dev/null +++ b/source/blender/python/manta_full/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 3 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, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/source/blender/python/manta_full/Doxyfile.in b/source/blender/python/manta_full/Doxyfile.in new file mode 100644 index 00000000000..19c9ebf2440 --- /dev/null +++ b/source/blender/python/manta_full/Doxyfile.in @@ -0,0 +1,1719 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = mantaflow + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.5 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "A framework for fluid simulation" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_SOURCE_DIR@/doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @CMAKE_CURRENT_BINARY_DIR@/doxy/source + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.cpp *.h *.cu + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = @CMAKE_CURRENT_BINARY_DIR@/doxy/source \ + @CMAKE_CURRENT_BINARY_DIR@/doxy/source/python \ + @CMAKE_CURRENT_BINARY_DIR@/doxy/source/gui \ + @CMAKE_CURRENT_BINARY_DIR@/doxy/source/util + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN=1 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/source/blender/python/manta_full/INSTALLING b/source/blender/python/manta_full/INSTALLING new file mode 100644 index 00000000000..2be30ee98fc --- /dev/null +++ b/source/blender/python/manta_full/INSTALLING @@ -0,0 +1,2 @@ +Detailed instructions on how to compile and run mantaflow can be found at:
+http://mantaflow.ethz.ch/quickstart.html
diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/adler32.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/adler32.c new file mode 100644 index 00000000000..a868f073d8a --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/adler32.c @@ -0,0 +1,179 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); + +#define BASE 65521 /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ +#ifdef NO_DIVIDE +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ + do { \ + CHOP(a); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD(a) \ + do { \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD28(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + + /* the derivation of this formula is left as an exercise for the reader */ + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/compress.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/compress.c new file mode 100644 index 00000000000..6e9762676a0 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/compress.c @@ -0,0 +1,80 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.c new file mode 100644 index 00000000000..979a7190a3c --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.c @@ -0,0 +1,425 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include <stdio.h> +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.h new file mode 100644 index 00000000000..9e0c7781025 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.c new file mode 100644 index 00000000000..696957705b7 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.c @@ -0,0 +1,1967 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + s = strm->state; + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateResetKeep (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + deflate_state *s; + int put; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len; + deflate_state *s = strm->state; + + _tr_flush_bits(s); + len = s->pending; + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s->lookahead <= MAX_MATCH) { + fill_window(s); + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.h new file mode 100644 index 00000000000..ce0299edd19 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/deflate.h @@ -0,0 +1,346 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2012 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzclose.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzclose.c new file mode 100644 index 00000000000..caeb99a3177 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzguts.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzguts.h new file mode 100644 index 00000000000..d87659d0319 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzguts.h @@ -0,0 +1,209 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include <stdio.h> +#include "zlib.h" +#ifdef STDC +# include <string.h> +# include <stdlib.h> +# include <limits.h> +#endif +#include <fcntl.h> + +#ifdef _WIN32 +# include <stddef.h> +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include <io.h> +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99, yet still not supported by + Microsoft more than a decade later!), _snprintf does not guarantee null + termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include <windows.h> +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include <errno.h> +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzlib.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzlib.c new file mode 100644 index 00000000000..fae202ef890 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzlib.c @@ -0,0 +1,634 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_WIN32) && !defined(__BORLANDC__) +# define LSEEK _lseeki64 +#else +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const void *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + state->x.have = 0; /* no output data available */ + if (state->mode == GZ_READ) { /* for reading ... */ + state->eof = 0; /* not at end of file */ + state->past = 0; /* have not read past end yet */ + state->how = LOOK; /* look for gzip header */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->x.pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const void *path; + int fd; + const char *mode; +{ + gz_statep state; + size_t len; + int oflag; +#ifdef O_CLOEXEC + int cloexec = 0; +#endif +#ifdef O_EXCL + int exclusive = 0; +#endif + + /* check input */ + if (path == NULL) + return NULL; + + /* allocate gzFile structure to return */ + state = (gz_statep)malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; +#ifdef O_CLOEXEC + case 'e': + cloexec = 1; + break; +#endif +#ifdef O_EXCL + case 'x': + exclusive = 1; + break; +#endif + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + break; + case 'T': + state->direct = 1; + break; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + + /* save the path name for error messages */ +#ifdef _WIN32 + if (fd == -2) { + len = wcstombs(NULL, path, 0); + if (len == (size_t)-1) + len = 0; + } + else +#endif + len = strlen((const char *)path); + state->path = (char *)malloc(len + 1); + if (state->path == NULL) { + free(state); + return NULL; + } +#ifdef _WIN32 + if (fd == -2) + if (len) + wcstombs(state->path, path, len + 1); + else + *(state->path) = 0; + else +#endif +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->path, len + 1, "%s", (const char *)path); +#else + strcpy(state->path, path); +#endif + + /* compute the flags for open() */ + oflag = +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif +#ifdef O_CLOEXEC + (cloexec ? O_CLOEXEC : 0) | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | +#ifdef O_EXCL + (exclusive ? O_EXCL : 0) | +#endif + (state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))); + + /* open the file with the appropriate flags (or just use fd) */ + state->fd = fd > -1 ? fd : ( +#ifdef _WIN32 + fd == -2 ? _wopen(path, oflag, 0666) : +#endif + open((const char *)path, oflag, 0666)); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd); /* for debugging */ +#else + sprintf(path, "<fd:%d>", fd); /* for debugging */ +#endif + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +#ifdef _WIN32 +gzFile ZEXPORT gzopen_w(path, mode) + const wchar_t *path; + const char *mode; +{ + return gz_open(path, -2, mode); +} +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size < 2) + size = 2; /* need two bytes to check magic header */ + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->x.pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->x.pos + offset >= 0) { + ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + if (ret == -1) + return -1; + state->x.have = 0; + state->eof = 0; + state->past = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->x.pos += offset; + return state->x.pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->x.pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? + (unsigned)offset : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->x.pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->x.pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? state->past : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->err == Z_MEM_ERROR ? "out of memory" : + (state->msg == NULL ? "" : state->msg); +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) { + state->eof = 0; + state->past = 0; + } + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + if (err != Z_OK && err != Z_BUF_ERROR) + state->x.have = 0; + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, return literal string when requested */ + if (err == Z_MEM_ERROR) + return; + + /* construct error message with path */ + if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == + NULL) { + state->err = Z_MEM_ERROR; + return; + } +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); +#else + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); +#endif + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzread.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzread.c new file mode 100644 index 00000000000..bf4538eb274 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzread.c @@ -0,0 +1,594 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_look OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_fetch OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + If strm->avail_in != 0, then the current data is moved to the beginning of + the input buffer, and then the remainder of the buffer is loaded with the + available data from the input file. */ +local int gz_avail(state) + gz_statep state; +{ + unsigned got; + z_streamp strm = &(state->strm); + + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + if (state->eof == 0) { + if (strm->avail_in) { /* copy what's there to the start */ + unsigned char *p = state->in; + unsigned const char *q = strm->next_in; + unsigned n = strm->avail_in; + do { + *p++ = *q++; + } while (--n); + } + if (gz_load(state, state->in + strm->avail_in, + state->size - strm->avail_in, &got) == -1) + return -1; + strm->avail_in += got; + strm->next_in = state->in; + } + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression. If direct copying, then leftover input + data from the input buffer will be copied to the output buffer. In that + case, all further file reads will be directly to either the output buffer or + a user buffer. If decompressing, the inflate state will be initialized. + gz_look() will return 0 on success or -1 on failure. */ +local int gz_look(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = (unsigned char *)malloc(state->want); + state->out = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get at least the magic bytes in the input buffer */ + if (strm->avail_in < 2) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for gzip magic bytes -- if there, do gzip decoding (note: there is + a logical dilemma here when considering the case of a partially written + gzip file, to wit, if a single 31 byte is written, then we cannot tell + whether this is a single-byte file, or just a partially written gzip + file -- for here we assume that if a gzip file is being written, then + the header will be written in a single operation, so that reading a + single byte is sufficient indication that it is not a gzip file) */ + if (strm->avail_in > 1 && + strm->next_in[0] == 31 && strm->next_in[1] == 139) { + inflateReset(strm); + state->how = GZIP; + state->direct = 0; + return 0; + } + + /* no gzip header -- if we were decoding gzip before, then this is trailing + garbage. Ignore the trailing garbage and finish. */ + if (state->direct == 0) { + strm->avail_in = 0; + state->eof = 1; + state->x.have = 0; + return 0; + } + + /* doing raw i/o, copy any leftover input to output -- this assumes that + the output buffer is larger than the input buffer, which also assures + space for gzungetc() */ + state->x.next = state->out; + if (strm->avail_in) { + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + On return, state->x.have and state->x.next point to the just decompressed + data. If the gzip stream completes, state->how is reset to LOOK to look for + the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + on success, -1 on failure. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret = Z_OK; + unsigned had; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_BUF_ERROR, "unexpected end of file"); + break; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output */ + state->x.have = had - strm->avail_out; + state->x.next = strm->next_out - state->x.have; + + /* if the gzip stream completed successfully, look for another */ + if (ret == Z_STREAM_END) + state->how = LOOK; + + /* good decompression */ + return 0; +} + +/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for to determine whether to copy or decompress. Returns -1 on error, + otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + end of the input file has been reached and all data has been processed. */ +local int gz_fetch(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + do { + switch(state->how) { + case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ + if (gz_look(state) == -1) + return -1; + if (state->how == LOOK) + return 0; + break; + case COPY: /* -> COPY */ + if (gz_load(state, state->out, state->size << 1, &(state->x.have)) + == -1) + return -1; + state->x.next = state->out; + return 0; + case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + } while (state->x.have == 0 && (!state->eof || strm->avail_in)); + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->x.have) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? + (unsigned)len : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->x.have) { + n = state->x.have > len ? len : state->x.have; + memcpy(buf, state->x.next, n); + state->x.next += n; + state->x.have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) { + state->past = 1; /* tried to read past end */ + break; + } + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + continue; /* no progress yet -- go back to copy above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = (unsigned char *)buf; + if (gz_decomp(state) == -1) + return -1; + n = state->x.have; + state->x.have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->x.pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +#else +# undef gzgetc +#endif +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->x.have) { + state->x.have--; + state->x.pos++; + return *(state->x.next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +int ZEXPORT gzgetc_(file) +gzFile file; +{ + return gzgetc(file); +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->x.have == 0) { + state->x.have = 1; + state->x.next = state->out + (state->size << 1) - 1; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->x.have == (state->size << 1)) { + gz_error(state, Z_DATA_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->x.next == state->out) { + unsigned char *src = state->out + state->x.have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->x.next = dest; + } + state->x.have++; + state->x.next--; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->x.have == 0 && gz_fetch(state) == -1) + return NULL; /* error */ + if (state->x.have == 0) { /* end of file */ + state->past = 1; /* read past end */ + break; /* return what we have */ + } + + /* look for end-of-line in current output buffer */ + n = state->x.have > left ? left : state->x.have; + eol = (unsigned char *)memchr(state->x.next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->x.next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->x.next, n); + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* return terminated string, or if nothing, end of file */ + if (buf == str) + return NULL; + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + + /* return 1 if transparent, 0 if processing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret, err; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : err; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzwrite.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzwrite.c new file mode 100644 index 00000000000..aa767fbf63e --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/gzwrite.c @@ -0,0 +1,577 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input buffer */ + state->in = (unsigned char *)malloc(state->want); + if (state->in == NULL) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = (unsigned char *)malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. If gz->direct + is true, then simply write to the output file without compressing, and + ignore flush. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* write directly if requested */ + if (state->direct) { + got = write(state->fd, strm->next_in, strm->avail_in); + if (got < 0 || (unsigned)got != strm->avail_in) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in = 0; + return 0; + } + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->x.next); + if (have && ((got = write(state->fd, state->x.next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->x.next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + unsigned have, copy; + + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + copy = state->size - have; + if (copy > len) + copy = len; + memcpy(state->in + have, buf, copy); + strm->avail_in += copy; + state->x.pos += copy; + buf = (const char *)buf + copy; + len -= copy; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (z_const Bytef *)buf; + state->x.pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned have; + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (have < state->size) { + state->in[have] = c; + strm->avail_in++; + state->x.pos++; + return c & 0xff; + } + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c & 0xff; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +#include <stdarg.h> + +/* -- see zlib.h -- */ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf((char *)(state->in), format, va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf((char *)(state->in), format, va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf((char *)(state->in), size, format, va); + len = strlen((char *)(state->in)); +# else + len = vsnprintf((char *)(state->in), size, format, va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) +{ + va_list va; + int ret; + + va_start(va, format); + ret = gzvprintf(file, format, va); + va_end(va); + return ret; +} + +#else /* !STDC && !Z_HAVE_STDARG_H */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that can really pass pointer in ints */ + if (sizeof(int) != sizeof(void *)) + return 0; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen((char *)(state->in)); +# else + len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, + a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = Z_OK; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + ret = state->err; + } + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1) + ret = state->err; + if (state->size) { + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + if (close(state->fd) == -1) + ret = Z_ERRNO; + free(state); + return ret; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/infback.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/infback.c new file mode 100644 index 00000000000..f3833c2e434 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/infback.c @@ -0,0 +1,640 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.c new file mode 100644 index 00000000000..bda59ceb6a1 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.h new file mode 100644 index 00000000000..e5c1aa4ca8c --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffixed.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffixed.h new file mode 100644 index 00000000000..d6283277694 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.c new file mode 100644 index 00000000000..870f89bb4d3 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.c @@ -0,0 +1,1512 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateResetKeep(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include <stdio.h> + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, end, copy) +z_streamp strm; +const Bytef *end; +unsigned copy; +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +const unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.h new file mode 100644 index 00000000000..95f4986d400 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.c new file mode 100644 index 00000000000..44d89cf24e1 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.c @@ -0,0 +1,306 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.h new file mode 100644 index 00000000000..baa53a0b1a1 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.c new file mode 100644 index 00000000000..1fd7759ef00 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.c @@ -0,0 +1,1226 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2012 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include <ctype.h> +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + _length_code[length++] = (uch)code; + } + } + Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + _dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include <stdio.h> +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.h new file mode 100644 index 00000000000..d35639d82a2 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/uncompr.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/uncompr.c new file mode 100644 index 00000000000..242e9493dff --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/uncompr.c @@ -0,0 +1,59 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/zconf.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zconf.h new file mode 100644 index 00000000000..9987a775530 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zconf.h @@ -0,0 +1,511 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include <limits.h> +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include <sys/types.h> /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include <stdarg.h> /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include <stddef.h> /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/zlib.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zlib.h new file mode 100644 index 00000000000..3e0c7672ac5 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.c b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.c new file mode 100644 index 00000000000..23d2ebef008 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.c @@ -0,0 +1,324 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +z_const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.h b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.h new file mode 100644 index 00000000000..24ab06b1cf6 --- /dev/null +++ b/source/blender/python/manta_full/dependencies/zlib-1.2.8/zutil.h @@ -0,0 +1,253 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include <stddef.h> +# endif +# include <string.h> +# include <stdlib.h> +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include <alloc.h> +# endif +# else /* MSC or DJGPP */ +# include <malloc.h> +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# if defined(M_I86) && !defined(Z_SOLO) +# include <malloc.h> +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include <stdio.h> + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/source/blender/python/manta_full/resources/pause.png b/source/blender/python/manta_full/resources/pause.png Binary files differnew file mode 100644 index 00000000000..53e0a0c4ca0 --- /dev/null +++ b/source/blender/python/manta_full/resources/pause.png diff --git a/source/blender/python/manta_full/resources/play.png b/source/blender/python/manta_full/resources/play.png Binary files differnew file mode 100644 index 00000000000..1383312473b --- /dev/null +++ b/source/blender/python/manta_full/resources/play.png diff --git a/source/blender/python/manta_full/resources/res.qrc b/source/blender/python/manta_full/resources/res.qrc new file mode 100644 index 00000000000..e39ebc0d6e2 --- /dev/null +++ b/source/blender/python/manta_full/resources/res.qrc @@ -0,0 +1,7 @@ + <!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>pause.png</file> + <file>stop.png</file> + <file>play.png</file> + </qresource> + </RCC>
\ No newline at end of file diff --git a/source/blender/python/manta_full/resources/stop.png b/source/blender/python/manta_full/resources/stop.png Binary files differnew file mode 100644 index 00000000000..efab55e1b91 --- /dev/null +++ b/source/blender/python/manta_full/resources/stop.png diff --git a/source/blender/python/manta_full/scenes/simpleplume.py b/source/blender/python/manta_full/scenes/simpleplume.py new file mode 100644 index 00000000000..459af24c074 --- /dev/null +++ b/source/blender/python/manta_full/scenes/simpleplume.py @@ -0,0 +1,61 @@ +# +# Simple example scene (hello world) +# Simulation of a buoyant smoke density plume + +#import pdb; pdb.set_trace() + +from manta import * + +# solver params +res = 64 +gs = vec3(res,1.5*res,res) +s = FluidSolver(name='main', gridSize = gs) +s.timestep = 1.0 + +# prepare grids +flags = s.create(FlagGrid) +vel = s.create(MACGrid) +density = s.create(RealGrid) +pressure = s.create(RealGrid) + +# noise field +noise = s.create(NoiseField, loadFromFile=True) +noise.posScale = vec3(45) +noise.clamp = True +noise.clampNeg = 0 +noise.clampPos = 1 +noise.valScale = 1 +noise.valOffset = 0.75 +noise.timeAnim = 0.2 + +flags.initDomain() +flags.fillGrid() + +if (GUI): + gui = Gui() + gui.show() + +source = s.create(Cylinder, center=gs*vec3(0.5,0.1,0.5), radius=res*0.14, z=gs*vec3(0, 0.02, 0)) + +#main loop +for t in range(250): + if t<100: + densityInflow(flags=flags, density=density, noise=noise, shape=source, scale=1, sigma=0.5) + + # optionally, enforce inflow velocity + #source.applyToGrid(grid=vel, value=velInflow) + + advectSemiLagrange(flags=flags, vel=vel, grid=density, order=2) + advectSemiLagrange(flags=flags, vel=vel, grid=vel , order=2, strength=1.0) + + setWallBcs(flags=flags, vel=vel) + addBuoyancy(density=density, vel=vel, gravity=vec3(0,-6e-4,0), flags=flags) + + solvePressure(flags=flags, vel=vel, pressure=pressure, useResNorm=True) + setWallBcs(flags=flags, vel=vel) + #density.save('den%04d.uni' % t) + + s.printTimings() + s.step() + + diff --git a/source/blender/python/manta_full/source/cmake/FindTBB.cmake b/source/blender/python/manta_full/source/cmake/FindTBB.cmake new file mode 100644 index 00000000000..f8bb2cebca5 --- /dev/null +++ b/source/blender/python/manta_full/source/cmake/FindTBB.cmake @@ -0,0 +1,267 @@ +# Locate Intel Threading Building Blocks include paths and libraries
+# FindTBB.cmake can be found at https://code.google.com/p/findtbb/
+# Written by Hannes Hofmann <hannes.hofmann _at_ informatik.uni-erlangen.de>
+# Improvements by Gino van den Bergen <gino _at_ dtecta.com>,
+# Florian Uhlig <F.Uhlig _at_ gsi.de>,
+# Jiri Marsik <jiri.marsik89 _at_ gmail.com>
+# Tobias Pfaff <tpfaff _at_ inf.ethz.ch>
+
+
+# The MIT License
+#
+# Copyright (c) 2011 Hannes Hofmann
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# GvdB: This module uses the environment variable TBB_ARCH_PLATFORM which defines architecture and compiler.
+# e.g. "ia32/vc8" or "em64t/cc4.1.0_libc2.4_kernel2.6.16.21"
+# TBB_ARCH_PLATFORM is set by the build script tbbvars[.bat|.sh|.csh], which can be found
+# in the TBB installation directory (TBB_INSTALL_DIR).
+#
+# GvdB: Mac OS X distribution places libraries directly in lib directory.
+#
+# For backwards compatibility, you may explicitely set the CMake variables TBB_ARCHITECTURE and TBB_COMPILER.
+# TBB_ARCHITECTURE [ ia32 | em64t | itanium ]
+# which architecture to use
+# TBB_COMPILER e.g. vc9 or cc3.2.3_libc2.3.2_kernel2.4.21 or cc4.0.1_os10.4.9
+# which compiler to use (detected automatically on Windows)
+
+# This module respects
+# TBB_INSTALL_DIR or $ENV{TBB21_INSTALL_DIR} or $ENV{TBB_INSTALL_DIR}
+
+# This module defines
+# TBB_INCLUDE_DIRS, where to find task_scheduler_init.h, etc.
+# TBB_LIBRARY_DIRS, where to find libtbb, libtbbmalloc
+# TBB_DEBUG_LIBRARY_DIRS, where to find libtbb_debug, libtbbmalloc_debug
+# TBB_INSTALL_DIR, the base TBB install directory
+# TBB_LIBRARIES, the libraries to link against to use TBB.
+# TBB_DEBUG_LIBRARIES, the libraries to link against to use TBB with debug symbols.
+# TBB_FOUND, If false, don't try to use TBB.
+# TBB_INTERFACE_VERSION, as defined in tbb/tbb_stddef.h
+
+
+if (WIN32)
+ # has em64t/vc8 em64t/vc9
+ # has ia32/vc7.1 ia32/vc8 ia32/vc9
+ set(_TBB_DEFAULT_INSTALL_DIR "C:/Program Files/Intel/TBB")
+ set(_TBB_LIB_NAME "tbb")
+ set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+ set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+ set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+ if (MSVC71)
+ set (_TBB_COMPILER "vc7.1")
+ endif(MSVC71)
+ if (MSVC80)
+ set(_TBB_COMPILER "vc8")
+ endif(MSVC80)
+ if (MSVC90)
+ set(_TBB_COMPILER "vc9")
+ endif(MSVC90)
+ if(MSVC10)
+ set(_TBB_COMPILER "vc10")
+ endif(MSVC10)
+ # Todo: add other Windows compilers such as ICL.
+ if (NOT TBB_ARCHITECTURE)
+ set(TBB_ARCHITECTURE "ia32")
+ endif()
+ set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+endif (WIN32)
+
+if (UNIX)
+ if (APPLE)
+ # MAC
+ set(_TBB_DEFAULT_INSTALL_DIR "/Library/Frameworks/Intel_TBB.framework/Versions")
+ # libs: libtbb.dylib, libtbbmalloc.dylib, *_debug
+ set(_TBB_LIB_NAME "tbb")
+ set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+ set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+ set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+ # default flavor on apple: ia32/cc4.0.1_os10.4.9
+ # Jiri: There is no reason to presume there is only one flavor and
+ # that user's setting of variables should be ignored.
+ if(NOT TBB_COMPILER)
+ set(_TBB_COMPILER "cc4.0.1_os10.4.9")
+ elseif (NOT TBB_COMPILER)
+ set(_TBB_COMPILER ${TBB_COMPILER})
+ endif(NOT TBB_COMPILER)
+ if(NOT TBB_ARCHITECTURE)
+ set(_TBB_ARCHITECTURE "ia32")
+ elseif(NOT TBB_ARCHITECTURE)
+ set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+ endif(NOT TBB_ARCHITECTURE)
+ else (APPLE)
+ # LINUX
+ set(_TBB_DEFAULT_INSTALL_DIR "/opt/intel/tbb" "/usr/local/include" "/usr/include")
+ set(_TBB_LIB_NAME "tbb")
+ set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+ set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+ set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+ # has em64t/cc3.2.3_libc2.3.2_kernel2.4.21 em64t/cc3.3.3_libc2.3.3_kernel2.6.5 em64t/cc3.4.3_libc2.3.4_kernel2.6.9 em64t/cc4.1.0_libc2.4_kernel2.6.16.21
+ # has ia32/*
+ # has itanium/*
+ set(_TBB_COMPILER ${TBB_COMPILER})
+ set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+ endif (APPLE)
+endif (UNIX)
+
+if (CMAKE_SYSTEM MATCHES "SunOS.*")
+# SUN
+# not yet supported
+# has em64t/cc3.4.3_kernel5.10
+# has ia32/*
+endif (CMAKE_SYSTEM MATCHES "SunOS.*")
+
+
+#-- Clear the public variables
+set (TBB_FOUND "NO")
+
+
+#-- Find TBB install dir and set ${_TBB_INSTALL_DIR} and cached ${TBB_INSTALL_DIR}
+# first: use CMake variable TBB_INSTALL_DIR
+if (TBB_INSTALL_DIR)
+ set (_TBB_INSTALL_DIR ${TBB_INSTALL_DIR})
+endif (TBB_INSTALL_DIR)
+# second: use environment variable
+if (NOT _TBB_INSTALL_DIR)
+ if (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "")
+ set (_TBB_INSTALL_DIR $ENV{TBB_INSTALL_DIR})
+ endif (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "")
+ # Intel recommends setting TBB21_INSTALL_DIR
+ if (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "")
+ set (_TBB_INSTALL_DIR $ENV{TBB21_INSTALL_DIR})
+ endif (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "")
+ if (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "")
+ set (_TBB_INSTALL_DIR $ENV{TBB22_INSTALL_DIR})
+ endif (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "")
+ if (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "")
+ set (_TBB_INSTALL_DIR $ENV{TBB30_INSTALL_DIR})
+ endif (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "")
+endif (NOT _TBB_INSTALL_DIR)
+# third: try to find path automatically
+if (NOT _TBB_INSTALL_DIR)
+ if (_TBB_DEFAULT_INSTALL_DIR)
+ set (_TBB_INSTALL_DIR ${_TBB_DEFAULT_INSTALL_DIR})
+ endif (_TBB_DEFAULT_INSTALL_DIR)
+endif (NOT _TBB_INSTALL_DIR)
+# sanity check
+if (NOT _TBB_INSTALL_DIR)
+ message ("ERROR: Unable to find Intel TBB install directory. ${_TBB_INSTALL_DIR}")
+else (NOT _TBB_INSTALL_DIR)
+# finally: set the cached CMake variable TBB_INSTALL_DIR
+if (NOT TBB_INSTALL_DIR)
+ set (TBB_INSTALL_DIR ${_TBB_INSTALL_DIR} CACHE PATH "Intel TBB install directory")
+ mark_as_advanced(TBB_INSTALL_DIR)
+endif (NOT TBB_INSTALL_DIR)
+
+#-- A macro to rewrite the paths of the library. This is necessary, because
+# find_library() always found the em64t/vc9 version of the TBB libs
+macro(TBB_CORRECT_LIB_DIR var_name)
+# if (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t")
+ string(REPLACE em64t "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}})
+# endif (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t")
+ string(REPLACE ia32 "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}})
+ string(REPLACE vc7.1 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+ string(REPLACE vc8 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+ string(REPLACE vc9 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+ string(REPLACE vc10 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+endmacro(TBB_CORRECT_LIB_DIR var_content)
+
+
+#-- Look for include directory and set ${TBB_INCLUDE_DIR}
+set (TBB_INC_SEARCH_DIR ${_TBB_INSTALL_DIR}/include)
+# Jiri: tbbvars now sets the CPATH environment variable to the directory
+# containing the headers.
+find_path(TBB_INCLUDE_DIR
+ tbb/task_scheduler_init.h
+ PATHS ${TBB_INC_SEARCH_DIR} ENV CPATH
+)
+mark_as_advanced(TBB_INCLUDE_DIR)
+
+set (_TBB_LIBRARY_DIR "${_TBB_INSTALL_DIR}/lib/${_TBB_ARCHITECTURE}/${_TBB_COMPILER}" ${_TBB_LIBRARY_DIR})
+
+# GvdB: Mac OS X distribution places libraries directly in lib directory.
+list(APPEND _TBB_LIBRARY_DIR ${_TBB_INSTALL_DIR}/lib)
+
+# Jiri: No reason not to check the default paths. From recent versions,
+# tbbvars has started exporting the LIBRARY_PATH and LD_LIBRARY_PATH
+# variables, which now point to the directories of the lib files.
+# It all makes more sense to use the ${_TBB_LIBRARY_DIR} as a HINTS
+# argument instead of the implicit PATHS as it isn't hard-coded
+# but computed by system introspection. Searching the LIBRARY_PATH
+# and LD_LIBRARY_PATH environment variables is now even more important
+# that tbbvars doesn't export TBB_ARCH_PLATFORM and it facilitates
+# the use of TBB built from sources.
+find_library(TBB_LIBRARY ${_TBB_LIB_NAME} HINTS ${_TBB_LIBRARY_DIR}
+ PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+find_library(TBB_MALLOC_LIBRARY ${_TBB_LIB_MALLOC_NAME} HINTS ${_TBB_LIBRARY_DIR}
+ PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+
+#Extract path from TBB_LIBRARY name
+get_filename_component(TBB_LIBRARY_DIR ${TBB_LIBRARY} PATH)
+
+#TBB_CORRECT_LIB_DIR(TBB_LIBRARY)
+#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY)
+mark_as_advanced(TBB_LIBRARY TBB_MALLOC_LIBRARY)
+
+#-- Look for debug libraries
+# Jiri: Changed the same way as for the release libraries.
+find_library(TBB_LIBRARY_DEBUG ${_TBB_LIB_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR}
+ PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+find_library(TBB_MALLOC_LIBRARY_DEBUG ${_TBB_LIB_MALLOC_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR}
+ PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+
+# Jiri: Self-built TBB stores the debug libraries in a separate directory.
+# Extract path from TBB_LIBRARY_DEBUG name
+get_filename_component(TBB_LIBRARY_DEBUG_DIR ${TBB_LIBRARY_DEBUG} PATH)
+
+#TBB_CORRECT_LIB_DIR(TBB_LIBRARY_DEBUG)
+#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY_DEBUG)
+mark_as_advanced(TBB_LIBRARY_DEBUG TBB_MALLOC_LIBRARY_DEBUG)
+
+if (TBB_INCLUDE_DIR)
+ if (TBB_LIBRARY)
+ set (TBB_FOUND "YES")
+ set (TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY} ${TBB_LIBRARIES})
+ set (TBB_DEBUG_LIBRARIES ${TBB_LIBRARY_DEBUG} ${TBB_MALLOC_LIBRARY_DEBUG} ${TBB_DEBUG_LIBRARIES})
+ set (TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR} CACHE PATH "TBB include directory" FORCE)
+ set (TBB_LIBRARY_DIRS ${TBB_LIBRARY_DIR} CACHE PATH "TBB library directory" FORCE)
+ # Jiri: Self-built TBB stores the debug libraries in a separate directory.
+ set (TBB_DEBUG_LIBRARY_DIRS ${TBB_LIBRARY_DEBUG_DIR} CACHE PATH "TBB debug library directory" FORCE)
+ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARY_DIRS TBB_DEBUG_LIBRARY_DIRS TBB_LIBRARIES TBB_DEBUG_LIBRARIES)
+ message(STATUS "Found Intel TBB")
+ endif (TBB_LIBRARY)
+endif (TBB_INCLUDE_DIR)
+
+if (NOT TBB_FOUND)
+ message("ERROR: Intel TBB NOT found!")
+ message(STATUS "Looked for Threading Building Blocks in ${_TBB_INSTALL_DIR}")
+ # do only throw fatal, if this pkg is REQUIRED
+ if (TBB_FIND_REQUIRED)
+ message(FATAL_ERROR "Could NOT find TBB library.")
+ endif (TBB_FIND_REQUIRED)
+endif (NOT TBB_FOUND)
+
+endif (NOT _TBB_INSTALL_DIR)
+
+if (TBB_FOUND)
+ set(TBB_INTERFACE_VERSION 0)
+ FILE(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _TBB_VERSION_CONTENTS)
+ STRING(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${_TBB_VERSION_CONTENTS}")
+ set(TBB_INTERFACE_VERSION "${TBB_INTERFACE_VERSION}")
+endif (TBB_FOUND)
diff --git a/source/blender/python/manta_full/source/commonkernels.h b/source/blender/python/manta_full/source/commonkernels.h new file mode 100644 index 00000000000..d2cfea768a2 --- /dev/null +++ b/source/blender/python/manta_full/source/commonkernels.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Common grid kernels + * + ******************************************************************************/ + +#ifndef _COMMONKERNELS_H +#define _COMMONKERNELS_H + +#include "general.h" +#include "kernel.h" +#include "grid.h" + +namespace Manta { + +//! Kernel: Invert real values, if positive and fluid +KERNEL(idx) +void InvertCheckFluid (FlagGrid& flags, Grid<Real>& grid) +{ + if (flags.isFluid(idx) && grid[idx] > 0) + grid[idx] = 1.0 / grid[idx]; +} + +//! Kernel: Squared sum over grid +KERNEL(idx, reduce=+) returns(double sum=0) +double GridSumSqr (Grid<Real>& grid) { + sum += square((double)grid[idx]); +} + +//! Kernel: rotation operator \nabla x v for centered vector fields +KERNEL(bnd=1) +void CurlOp (const Grid<Vec3>& grid, Grid<Vec3>& dst) { + Vec3 v = Vec3(0. , 0. , + 0.5*((grid(i+1,j,k).y - grid(i-1,j,k).y) - (grid(i,j+1,k).x - grid(i,j-1,k).x)) ); + if(dst.is3D()) { + v[0] = 0.5*((grid(i,j+1,k).z - grid(i,j-1,k).z) - (grid(i,j,k+1).y - grid(i,j,k-1).y)); + v[1] = 0.5*((grid(i,j,k+1).x - grid(i,j,k-1).x) - (grid(i+1,j,k).z - grid(i-1,j,k).z)); + } + dst(i,j,k) = v; +}; + +//! Kernel: divergence operator (from MAC grid) +KERNEL(bnd=1) +void DivergenceOpMAC(Grid<Real>& div, const MACGrid& grid) { + Vec3 del = Vec3(grid(i+1,j,k).x, grid(i,j+1,k).y, 0.) - grid(i,j,k); + if(grid.is3D()) del[2] += grid(i,j,k+1).z; + else del[2] = 0.; + div(i,j,k) = del.x + del.y + del.z; +} + +//! Kernel: gradient operator for MAC grid +KERNEL(bnd=1)void GradientOpMAC(MACGrid& gradient, const Grid<Real>& grid) { + Vec3 grad = (Vec3(grid(i,j,k)) - Vec3(grid(i-1,j,k), grid(i,j-1,k), 0. )); + if(grid.is3D()) grad[2] -= grid(i,j,k-1); + else grad[2] = 0.; + gradient(i,j,k) = grad; +} + +//! Kernel: centered gradient operator +KERNEL(bnd=1) void GradientOp(Grid<Vec3>& gradient, const Grid<Real>& grid) { + Vec3 grad = 0.5 * Vec3( grid(i+1,j,k)-grid(i-1,j,k), + grid(i,j+1,k)-grid(i,j-1,k), 0.); + if(grid.is3D()) grad[2]= 0.5*( grid(i,j,k+1)-grid(i,j,k-1) ); + gradient(i,j,k) = grad; +} + +//! Kernel: Laplace operator +KERNEL (bnd=1) void LaplaceOp(Grid<Real>& laplace, const Grid<Real>& grid) { + laplace(i,j,k) = -(6.0*grid(i,j,k)-grid(i+1,j,k)-grid(i-1,j,k)-grid(i,j+1,k)-grid(i,j-1,k)-grid(i,j,k+1)-grid(i,j,k-1)); +} + +//! Kernel: get component at MAC positions +KERNEL(bnd=1) void GetShiftedComponent(const Grid<Vec3>& grid, Grid<Real>& comp, int dim) { + Vec3i ishift(i,j,k); + ishift[dim]--; + comp(i,j,k) = 0.5*(grid(i,j,k)[dim] + grid(ishift)[dim]); +}; + +//! Kernel: get component (not shifted) +KERNEL(idx) void GetComponent(const Grid<Vec3>& grid, Grid<Real>& comp, int dim) { + comp[idx] = grid[idx][dim]; +}; + +//! Kernel: get norm of centered grid +KERNEL(idx) void GridNorm(Grid<Real>& n, const Grid<Vec3>& grid) { + n[idx] = norm(grid[idx]); +}; + +//! Kernel: set component (not shifted) +KERNEL(idx) void SetComponent(Grid<Vec3>& grid, const Grid<Real>& comp, int dim) { + grid[idx][dim] = comp[idx]; +}; + +//! Kernel: compute centered velocity field from MAC +KERNEL(bnd=1) void GetCentered(Grid<Vec3>& center, const MACGrid& vel) { + Vec3 v = 0.5 * ( vel(i,j,k) + Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, 0. ) ); + if(vel.is3D()) v[2] += 0.5 * vel(i,j,k+1).z; + else v[2] = 0.; + center(i,j,k) = v; +}; + +//! Kernel: compute MAC from centered velocity field +KERNEL(bnd=1) void GetMAC(MACGrid& vel, const Grid<Vec3>& center) { + Vec3 v = 0.5*(center(i,j,k) + Vec3(center(i-1,j,k).x, center(i,j-1,k).y, 0. )); + if(vel.is3D()) v[2] += 0.5 * center(i,j,k-1).z; + else v[2] = 0.; + vel(i,j,k) = v; +}; + +//! Fill in the domain boundary cells (i,j,k=0/size-1) from the neighboring cells +KERNEL void FillInBoundary(Grid<Vec3>& grid, int g) { + if (i==0) grid(i,j,k) = grid(i+1,j,k); + if (j==0) grid(i,j,k) = grid(i,j+1,k); + if (k==0) grid(i,j,k) = grid(i,j,k+1); + if (i==grid.getSizeX()-1) grid(i,j,k) = grid(i-1,j,k); + if (j==grid.getSizeY()-1) grid(i,j,k) = grid(i,j-1,k); + if (k==grid.getSizeZ()-1) grid(i,j,k) = grid(i,j,k-1); +} + +} // namespace +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/conjugategrad.cpp b/source/blender/python/manta_full/source/conjugategrad.cpp new file mode 100644 index 00000000000..ee432b47259 --- /dev/null +++ b/source/blender/python/manta_full/source/conjugategrad.cpp @@ -0,0 +1,303 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Conjugate gradient solver + * + ******************************************************************************/ + +#include "conjugategrad.h" +#include "commonkernels.h" + +using namespace std; +namespace Manta { + +const int CG_DEBUGLEVEL = 4; + +//***************************************************************************** +// Precondition helpers + +//! Preconditioning a la Wavelet Turbulence (needs 4 add. grids) +void InitPreconditionIncompCholesky(FlagGrid& flags, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, + Grid<Real>& orgA0, Grid<Real>& orgAi, Grid<Real>& orgAj, Grid<Real>& orgAk) +{ + // compute IC according to Golub and Van Loan + A0 = orgA0; + Ai = orgAi; + Aj = orgAj; + Ak = orgAk; + + FOR_IJK(A0) { + if (flags.isFluid(i,j,k)) { + const int idx = A0.index(i,j,k); + A0[idx] = sqrt(A0[idx]); + + // correct left and top stencil in other entries + // for i = k+1:n + // if (A(i,k) != 0) + // A(i,k) = A(i,k) / A(k,k) + Real invDiagonal = 1.0f / A0[idx]; + Ai[idx] *= invDiagonal; + Aj[idx] *= invDiagonal; + Ak[idx] *= invDiagonal; + + // correct the right and bottom stencil in other entries + // for j = k+1:n + // for i = j:n + // if (A(i,j) != 0) + // A(i,j) = A(i,j) - A(i,k) * A(j,k) + A0(i+1,j,k) -= square(Ai[idx]); + A0(i,j+1,k) -= square(Aj[idx]); + A0(i,j,k+1) -= square(Ak[idx]); + } + } + + // invert A0 for faster computation later + InvertCheckFluid (flags, A0); +}; + +//! Preconditioning using modified IC ala Bridson (needs 1 add. grid) +void InitPreconditionModifiedIncompCholesky2(FlagGrid& flags, + Grid<Real>&Aprecond, + Grid<Real>&A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + // compute IC according to Golub and Van Loan + Aprecond.clear(); + + FOR_IJK(flags) { + if (!flags.isFluid(i,j,k)) continue; + + const Real tau = 0.97; + const Real sigma = 0.25; + + // compute modified incomplete cholesky + Real e = 0.; + e = A0(i,j,k) + - square(Ai(i-1,j,k) * Aprecond(i-1,j,k) ) + - square(Aj(i,j-1,k) * Aprecond(i,j-1,k) ) + - square(Ak(i,j,k-1) * Aprecond(i,j,k-1) ) ; + e -= tau * ( + Ai(i-1,j,k) * ( Aj(i-1,j,k) + Ak(i-1,j,k) )* square( Aprecond(i-1,j,k) ) + + Aj(i,j-1,k) * ( Ai(i,j-1,k) + Ak(i,j-1,k) )* square( Aprecond(i,j-1,k) ) + + Ak(i,j,k-1) * ( Ai(i,j,k-1) + Aj(i,j,k-1) )* square( Aprecond(i,j,k-1) ) + + 0. ); + + // stability cutoff + if(e < sigma * A0(i,j,k)) + e = A0(i,j,k); + + Aprecond(i,j,k) = 1. / sqrt( e ); + } +}; + +//! Apply WT-style ICP +void ApplyPreconditionIncompCholesky(Grid<Real>& dst, Grid<Real>& Var1, FlagGrid& flags, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, + Grid<Real>& orgA0, Grid<Real>& orgAi, Grid<Real>& orgAj, Grid<Real>& orgAk) +{ + + // forward substitution + FOR_IJK(dst) { + if (!flags.isFluid(i,j,k)) continue; + dst(i,j,k) = A0(i,j,k) * (Var1(i,j,k) + - dst(i-1,j,k) * Ai(i-1,j,k) + - dst(i,j-1,k) * Aj(i,j-1,k) + - dst(i,j,k-1) * Ak(i,j,k-1)); + } + + // backward substitution + FOR_IJK_REVERSE(dst) { + const int idx = A0.index(i,j,k); + if (!flags.isFluid(idx)) continue; + dst[idx] = A0[idx] * ( dst[idx] + - dst(i+1,j,k) * Ai[idx] + - dst(i,j+1,k) * Aj[idx] + - dst(i,j,k+1) * Ak[idx]); + } +} + +//! Apply Bridson-style mICP +void ApplyPreconditionModifiedIncompCholesky2(Grid<Real>& dst, Grid<Real>& Var1, FlagGrid& flags, + Grid<Real>& Aprecond, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + // forward substitution + FOR_IJK(dst) { + if (!flags.isFluid(i,j,k)) continue; + const Real p = Aprecond(i,j,k); + dst(i,j,k) = p * (Var1(i,j,k) + - dst(i-1,j,k) * Ai(i-1,j,k) * Aprecond(i-1,j,k) + - dst(i,j-1,k) * Aj(i,j-1,k) * Aprecond(i,j-1,k) + - dst(i,j,k-1) * Ak(i,j,k-1) * Aprecond(i,j,k-1) ); + } + + // backward substitution + FOR_IJK_REVERSE(dst) { + const int idx = A0.index(i,j,k); + if (!flags.isFluid(idx)) continue; + const Real p = Aprecond[idx]; + dst[idx] = p * ( dst[idx] + - dst(i+1,j,k) * Ai[idx] * p + - dst(i,j+1,k) * Aj[idx] * p + - dst(i,j,k+1) * Ak[idx] * p); + } +} + + +//***************************************************************************** +// Kernels + +//! Kernel: Compute the dot product between two Real grids +/*! Uses double precision internally */ +KERNEL(idx, reduce=+) returns(double result=0.0) +double GridDotProduct (const Grid<Real>& a, const Grid<Real>& b) { + result += (a[idx] * b[idx]); +}; + +//! Kernel: compute residual (init) and add to sigma +KERNEL(idx, reduce=+) returns(double sigma=0) +double InitSigma (FlagGrid& flags, Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& temp) +{ + const double res = rhs[idx] - temp[idx]; + dst[idx] = (Real)res; + + // only compute residual in fluid region + if(flags.isFluid(idx)) + sigma += res*res; +}; + +//! Kernel: update search vector +KERNEL(idx) void UpdateSearchVec (Grid<Real>& dst, Grid<Real>& src, Real factor) +{ + dst[idx] = src[idx] + factor * dst[idx]; +} + +//***************************************************************************** +// CG class + +template<class APPLYMAT> +GridCg<APPLYMAT>::GridCg(Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& residual, Grid<Real>& search, FlagGrid& flags, Grid<Real>& tmp, + Grid<Real>* pA0, Grid<Real>* pAi, Grid<Real>* pAj, Grid<Real>* pAk) : + GridCgInterface(), mInited(false), mIterations(0), mDst(dst), mRhs(rhs), mResidual(residual), + mSearch(search), mFlags(flags), mTmp(tmp), mpA0(pA0), mpAi(pAi), mpAj(pAj), mpAk(pAk), + mPcMethod(PC_None), mpPCA0(pA0), mpPCAi(pAi), mpPCAj(pAj), mpPCAk(pAk), mSigma(0.), mAccuracy(VECTOR_EPSILON), mResNorm(1e20) +{ + dst.clear(); + residual.clear(); + search.clear(); + tmp.clear(); +} + +template<class APPLYMAT> +void GridCg<APPLYMAT>::doInit() { + mInited = true; + + mResidual = mRhs; // p=0, residual = b + + if (mPcMethod == PC_ICP) { + assertMsg(mDst.is3D(), "ICP only supports 3D grids so far"); + InitPreconditionIncompCholesky(mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + ApplyPreconditionIncompCholesky(mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + } else if (mPcMethod == PC_mICP) { + assertMsg(mDst.is3D(), "mICP only supports 3D grids so far"); + InitPreconditionModifiedIncompCholesky2(mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + ApplyPreconditionModifiedIncompCholesky2(mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + } else { + mTmp = mResidual; + } + + mSearch = mTmp; + + mSigma = GridDotProduct(mTmp, mResidual); +} + +template<class APPLYMAT> +bool GridCg<APPLYMAT>::iterate() { + if(!mInited) doInit(); + + mIterations++; + + // create matrix application operator passed as template argument, + // this could reinterpret the mpA pointers (not so clean right now) + // tmp = applyMat(search) + + APPLYMAT (mFlags, mTmp, mSearch, *mpA0, *mpAi, *mpAj, *mpAk); + + // alpha = sigma/dot(tmp, search) + Real dp = GridDotProduct(mTmp, mSearch); + Real alpha = 0.; + if(fabs(dp)>0.) alpha = mSigma / (Real)dp; + + gridScaledAdd<Real,Real>(mDst, mSearch, alpha); // dst += search * alpha + gridScaledAdd<Real,Real>(mResidual, mTmp, -alpha); // residual += tmp * -alpha + + if (mPcMethod == PC_ICP) + ApplyPreconditionIncompCholesky(mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + else if (mPcMethod == PC_mICP) + ApplyPreconditionModifiedIncompCholesky2(mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + else + mTmp = mResidual; + + // compute norm of the residual? + if(this->mUseResNorm) { + mResNorm = GridSumSqr(mResidual).sum; + } else { + mResNorm = mResidual.getMaxAbsValue(); + } + //if(mIterations % 10 == 9) debMsg("GridCg::Iteration i="<<mIterations<<", resNorm="<<mResNorm<<" accuracy="<<mAccuracy, 1); + + // abort here to safe some work... + if(mResNorm<mAccuracy) { + mSigma = mResNorm; // this will be returned later on to the caller... + return false; + } + + Real sigmaNew = GridDotProduct(mTmp, mResidual); + Real beta = sigmaNew / mSigma; + + // search = tmp + beta * search + UpdateSearchVec (mSearch, mTmp, beta); + + debMsg("PB-Cg::iter i="<<mIterations<<" sigmaNew="<<sigmaNew<<" sigmaLast="<<mSigma<<" alpha="<<alpha<<" beta="<<beta<<" ", CG_DEBUGLEVEL); + mSigma = sigmaNew; + + //debMsg("PB-CG-Norms::p"<<sqrt( GridOpNormNosqrt(mpDst, mpFlags).getValue() ) <<" search"<<sqrt( GridOpNormNosqrt(mpSearch, mpFlags).getValue(), CG_DEBUGLEVEL ) <<" res"<<sqrt( GridOpNormNosqrt(mpResidual, mpFlags).getValue() ) <<" tmp"<<sqrt( GridOpNormNosqrt(mpTmp, mpFlags).getValue() ), CG_DEBUGLEVEL ); // debug + return true; +} + +template<class APPLYMAT> +void GridCg<APPLYMAT>::solve(int maxIter) { + for (int iter=0; iter<maxIter; iter++) { + if (!iterate()) iter=maxIter; + } + return; +} + +static bool gPrint2dWarning = true; +template<class APPLYMAT> +void GridCg<APPLYMAT>::setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak) { + mPcMethod = method; + if( (!A0->is3D()) && (mPcMethod!=PC_None) ) { + if(gPrint2dWarning) { + debMsg("Pre-conditioning only supported in 3D for now, disabling it.", 1); + gPrint2dWarning = false; + } + mPcMethod=PC_None; + } + mpPCA0 = A0; + mpPCAi = Ai; + mpPCAj = Aj; + mpPCAk = Ak; +} + +// explicit instantiation +template class GridCg<ApplyMatrix>; +template class GridCg<ApplyMatrix2D>; + +}; // DDF diff --git a/source/blender/python/manta_full/source/conjugategrad.h b/source/blender/python/manta_full/source/conjugategrad.h new file mode 100644 index 00000000000..2fede9658d3 --- /dev/null +++ b/source/blender/python/manta_full/source/conjugategrad.h @@ -0,0 +1,169 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Conjugate gradient solver + * + ******************************************************************************/ + +#ifndef _CONJUGATEGRADIENT_H +#define _CONJUGATEGRADIENT_H + +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" + +namespace Manta { + +static const bool CG_DEBUG = false; + +//! Basic CG interface +class GridCgInterface { + public: + enum PreconditionType { PC_None=0, PC_ICP, PC_mICP }; + + GridCgInterface() : mUseResNorm(true) {}; + virtual ~GridCgInterface() {}; + + // solving functions + virtual bool iterate() = 0; + virtual void solve(int maxIter) = 0; + + // precond + virtual void setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak) = 0; + + // access + virtual Real getSigma() const = 0; + virtual Real getIterations() const = 0; + virtual Real getResNorm() const = 0; + virtual void setAccuracy(Real set) = 0; + virtual Real getAccuracy() const = 0; + + void setUseResNorm(bool set) { mUseResNorm = set; } + + protected: + + // use norm of residual, or max value for threshold? + bool mUseResNorm; +}; + + +//! Run single iteration of the cg solver +/*! the template argument determines the type of matrix multiplication, + typically a ApplyMatrix kernel, another one is needed e.g. for the + mesh-based wave equation solver */ +template<class APPLYMAT> +class GridCg : public GridCgInterface { + public: + //! constructor + GridCg(Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& residual, Grid<Real>& search, FlagGrid& flags, Grid<Real>& tmp, + Grid<Real>* A0, Grid<Real>* pAi, Grid<Real>* pAj, Grid<Real>* pAk); + ~GridCg() {} + + void doInit(); + bool iterate(); + void solve(int maxIter); + //! init pointers, and copy values from "normal" matrix + void setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak); + + // Accessors + Real getSigma() const { return mSigma; } + Real getIterations() const { return mIterations; } + + Real getResNorm() const { return mResNorm; } + + void setAccuracy(Real set) { mAccuracy=set; } + Real getAccuracy() const { return mAccuracy; } + + protected: + bool mInited; + int mIterations; + // grids + Grid<Real>& mDst; + Grid<Real>& mRhs; + Grid<Real>& mResidual; + Grid<Real>& mSearch; + FlagGrid& mFlags; + Grid<Real>& mTmp; + + Grid<Real> *mpA0, *mpAi, *mpAj, *mpAk; + + PreconditionType mPcMethod; + //! preconditioning grids + Grid<Real> *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk; + + //! sigma / residual + Real mSigma; + //! accuracy of solver (max. residuum) + Real mAccuracy; + //! norm of the residual + Real mResNorm; +}; // GridCg + + +//! Kernel: Apply symmetric stored Matrix +KERNEL(idx) +void ApplyMatrix (FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + if (!flags.isFluid(idx)) { + dst[idx] = src[idx]; + return; + } + dst[idx] = src[idx] * A0[idx] + + src[idx-X] * Ai[idx-X] + + src[idx+X] * Ai[idx] + + src[idx-Y] * Aj[idx-Y] + + src[idx+Y] * Aj[idx] + + src[idx-Z] * Ak[idx-Z] + + src[idx+Z] * Ak[idx]; +} + +//! Kernel: Apply symmetric stored Matrix. 2D version +KERNEL(idx) +void ApplyMatrix2D (FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + unusedParameter(Ak); // only there for parameter compatibility with ApplyMatrix + + if (!flags.isFluid(idx)) { + dst[idx] = src[idx]; + return; + } + dst[idx] = src[idx] * A0[idx] + + src[idx-X] * Ai[idx-X] + + src[idx+X] * Ai[idx] + + src[idx-Y] * Aj[idx-Y] + + src[idx+Y] * Aj[idx]; +} + +//! Kernel: Construct the matrix for the poisson equation +KERNEL (bnd=1) +void MakeLaplaceMatrix(FlagGrid& flags, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) { + if (!flags.isFluid(i,j,k)) + return; + + // center + if (!flags.isObstacle(i-1,j,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i+1,j,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i,j-1,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i,j+1,k)) A0(i,j,k) += 1.; + if (flags.is3D() && !flags.isObstacle(i,j,k-1)) A0(i,j,k) += 1.; + if (flags.is3D() && !flags.isObstacle(i,j,k+1)) A0(i,j,k) += 1.; + + if (flags.isFluid(i+1,j,k)) Ai(i,j,k) = -1.; + if (flags.isFluid(i,j+1,k)) Aj(i,j,k) = -1.; + if (flags.is3D() && flags.isFluid(i,j,k+1)) Ak(i,j,k) = -1.; +} + + + + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/buoyancy.cu b/source/blender/python/manta_full/source/cuda/buoyancy.cu new file mode 100644 index 00000000000..41f49f38f50 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/buoyancy.cu @@ -0,0 +1,421 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * CUDA functions for meshes + * + ******************************************************************************/ + +#include <iostream> +#include <map> +#include "cudatools.h" +#include "vortexsheet.h" + +using namespace std; + +namespace Manta { + + +const int VortKernelBlockSize = 512; + +//! for each Node, integrate all triangles +//! [for simplicity, expects array with multiples of VortKernelBlockSize] +__global__ void VorticityKernel(CVec3Ptr nodes, const CVec3Ptr vortexPos, const CVec3Ptr strength, float reg2, float cutoff2, + const int len, const int triBlocks, const int offset) { + const int nodeIdx = threadIdx.x + VortKernelBlockSize * blockIdx.x + offset; + + __shared__ float3 vpos[VortKernelBlockSize]; + __shared__ float3 vstr[VortKernelBlockSize]; + + // load current position + const int triLocal = threadIdx.x; + float3 pos = nodes.get(nodeIdx); + float3 u = pos; + + // divide triangles into blocks for shared memeory usage + for (int i=0; i<triBlocks; i++) { + const int triGlobal = threadIdx.x + VortKernelBlockSize * i; + + // load shared data + vpos[triLocal] = vortexPos.get(triGlobal); + vstr[triLocal] = strength.get(triGlobal); + __syncthreads(); + + //if (nodeIdx < len) { + for (int j=0; j<VortKernelBlockSize; j++) { + // actual vorticity kernel + float3 r = pos - vpos[j]; + float r2 = normSqr(r); + if (r2 < cutoff2) { + float l = r2+reg2, l2=l*l; + float div = rsqrtf(l2*l); + u += cross (vstr[j], r) * div; + } + } + //} + __syncthreads(); + } + + // writeback + nodes.set(nodeIdx, u); +} + +PYTHON void meshApplyBuoyancyTotalCuda(VortexSheetMesh& mesh, + Real scale=1e-3, Real regularization=1, Real cutoffCells = 1e10, bool useDiff = false) +{ + Real dt = parent->getDt(); + // align len to blocksize + int nodelen = ((mesh.numNodes() - 1) / VortKernelBlockSize + 1) * VortKernelBlockSize; + int trilen = ((mesh.numTris() - 1) / VortKernelBlockSize + 1) * VortKernelBlockSize; + + // construct device arrays + CVec3Array nodes(nodelen); + CVec3Array tripos(trilen); + CVec3Array strength(trilen); + + // copy values from mesh + for(size_t i=0; i<mesh.numTris(); i++) { + Vec3 center = mesh.getFaceCenter(i); + Vec3 vort = mesh.sheet(i).vorticity; + if (useDiff) + vort -= mesh.sheet(i).vorticitySmoothed; + Vec3 str = vort * (mesh.getFaceArea(i) * scale * dt); + + tripos.set(i, center); + strength.set(i, str); + } + for(size_t i=0; i<mesh.numNodes(); i++) + nodes.set(i, mesh.nodes(i).pos); + + // fill aligned parts + for(int i=mesh.numTris(); i<trilen; i++) { tripos.set(i, Vec3::Zero); strength.set(i, Vec3::Zero); } + for(int i=mesh.numNodes(); i<nodelen; i++) { nodes.set(i, Vec3::Zero); } + + nodes.upload(); + tripos.upload(); + strength.upload(); + + // construct parameters + float reg2=regularization*regularization; + float cutoff2 = cutoffCells*cutoffCells; + + // to avoid timeout, divide launches + const int diff = 15000000 / tripos.size() * VortKernelBlockSize; + cout << "Mesh buoyancy start" << endl; + for (int offset=0; offset<nodelen; offset+= diff) { + int blocks = (min(nodelen-offset, diff) - 1) / VortKernelBlockSize + 1; + + // invoke kernel + VorticityKernel<<<blocks, VortKernelBlockSize>>> (nodes.data(), tripos.data(), strength.data(), reg2, cutoff2, mesh.numNodes(), trilen/VortKernelBlockSize, offset); + } + cout << "Mesh buoyancy: " << (nodelen/diff)+1 << " calls" << endl; + nodes.download(); + + // readback new node positions + for(size_t i=0; i<mesh.numNodes(); i++) { + if (!mesh.isNodeFixed(i)) + mesh.nodes(i).pos = nodes[i]; + } +} + + +const int FvnBlockSize = 512; +const int FastEvalGridDivider = 2; + +//! for each Node, integrate all triangles +//! use block cell structure for fast pruning +__global__ void FastVorticityKernelNoshare(CVec3Ptr nodes, const CVec3Ptr srcPos, const CVec3Ptr strength, const int* srcStart, const int* srcLen, + float reg2, float cutoff2, float safeRadius2, int intRadius, int3 gridRes, float maxres, const int len, const int divider, const int offset) +{ + const int nodeIdx = threadIdx.x + FvnBlockSize * blockIdx.x + offset; + if (nodeIdx >= len) return; + + // load current node position + float3 pos = nodes.get(nodeIdx); + int3 cell = make_int3((int)pos.x / divider, (int)pos.y / divider, (int)pos.z / divider); + float3 u = pos; + + // query cells within block radius + int3 minBlock = make_int3(max(cell.x - intRadius,0), max(cell.y - intRadius,0), max(cell.z - intRadius,0)); + int3 maxBlock = make_int3(min(cell.x + intRadius, gridRes.x - 1), min(cell.y + intRadius, gridRes.y - 1), min(cell.z + intRadius, gridRes.z - 1)); + for (int i=minBlock.x; i<=maxBlock.x; i++) + for (int j=minBlock.y; j<=maxBlock.y; j++) + for (int k=minBlock.z; k<=maxBlock.z; k++) { + + // test if block is within radius + float3 d = make_float3(cell.x-i, cell.y-j, cell.z-k); + if (normSqr(d) > safeRadius2) continue; + + // find source cell, and divide it into thread blocks + int block = i + gridRes.x * (j + gridRes.y * k); + int slen = srcLen[block]; + if (slen == 0) continue; + int start = srcStart[block]; + + // process sources + for(int s=0; s<slen; s++) { + // actual vorticity kernel + float3 r = pos - srcPos.get(start+s); + float r2 = normSqr(r); + if (r2 < cutoff2) { + float l = r2+reg2, l2=l*l; + float div = rsqrtf(l2*l); + u += cross (strength.get(start+s), r) * div; + } + } + } + + + // writeback + nodes.set(nodeIdx, u); +} + +inline int cIndex(const Vec3& pos, const Vec3i& s) { + Vec3i p = toVec3i(pos) / FastEvalGridDivider; + if (p.x < 0 || p.y < 0 || p.z < 0 || p.x >= s.x || p.y >= s.y || p.z >= s.z) return -1; + return p.x + s.x * (p.y + s.y * p.z); +} + +// TODO: don't reorder nodes -- performance ? +PYTHON void meshApplyBuoyancyLocalCuda(VortexSheetMesh& mesh, + Real scale=1e-3, int cutoffCells=5, Real regularization=1) +{ + Real dt = parent->getDt(); + + // prepare center values and strength + vector<Vec3> center(mesh.numTris()); + vector<Vec3> strength(mesh.numTris()); + for(size_t i=0; i<mesh.numTris(); i++) { + Vec3 vort = mesh.sheet(i).vorticity - mesh.sheet(i).vorticitySmoothed; + strength[i] = vort * (mesh.getFaceArea(i) * scale * dt); + center[i] = mesh.getFaceCenter(i); + } + + // create triangles(sources) lookup grid + Vec3i gridRes = parent->getGridSize() / FastEvalGridDivider; + const int numCells = gridRes.x * gridRes.y * gridRes.z; + + // 1. count sources per cell + CArray<int> srcPerCell(numCells); + Real maxres = gridRes.max(); + for (size_t i=0; i<center.size(); i++) { + int cell = cIndex(center[i], gridRes); + if (cell >= 0) + srcPerCell[cell]++; + } + srcPerCell.upload(); + + // 2. create start index lookup + CArray<int> srcCellStart(numCells); + int cnt=0; + for (int i=0; i<numCells; i++) { + srcCellStart[i] = cnt; + cnt += srcPerCell[i]; + } + srcCellStart.upload(); + + // 3. reorder sources + CVec3Array reorderStrength(center.size()); + CVec3Array reorderSourcePos(center.size()); + { + vector<int> curSrcCell(numCells); + for (int i=0; i<(int)center.size(); i++) { + int cell = cIndex(center[i], gridRes); + if (cell < 0) continue; + int idx = srcCellStart[cell] + curSrcCell[cell]; + reorderStrength.set(idx, strength[i]); + reorderSourcePos.set(idx, center[i]); + curSrcCell[cell]++; + } + } + reorderStrength.upload(); + reorderSourcePos.upload(); + + // group nodes into blocks + // 1. count nodes per cell + vector<int> nodesPerCell(numCells); + for (int i=0; i<mesh.numNodes(); i++) { + int cell = cIndex(mesh.nodes(i).pos, gridRes); + if (cell >= 0) + nodesPerCell[cell]++; + } + // 2. cluster blocks into execution plan + vector<int> nodesCellStart(numCells); + int offset = 0; + for (int i=0; i<numCells; i++) { + nodesCellStart[i] = offset; + offset += nodesPerCell[i]; + } + // 3. reorder nodes + CVec3Array reorderNodes(mesh.numNodes()); + { + vector<int> curNodeCell(numCells); + for (int i=0; i<mesh.numNodes(); i++) { + int cell = cIndex(mesh.nodes(i).pos, gridRes); + if (cell < 0) continue; + int idx = nodesCellStart[cell] + curNodeCell[cell]; + reorderNodes.set(idx, mesh.nodes(i).pos); + curNodeCell[cell]++; + } + } + reorderNodes.upload(); + + // construct parameters + int cutoffInCells = cutoffCells / FastEvalGridDivider; // translate to lookup grid + float safeRadius = (float)cutoffCells / (float)FastEvalGridDivider + sqrt(3.0); + float safeRadius2 = safeRadius*safeRadius; + float reg2 = regularization*regularization; + float cutoff2 = cutoffCells*cutoffCells; + cout << "cutoff int " <<cutoffInCells << " safe " << safeRadius << " cutoff " << cutoffCells << endl; + + // call in chunks for prevent timeout + const int MAXBLOCK = 100; + const int diff = MAXBLOCK*FvnBlockSize; + int rcnt = 0; + for (int offset=0; offset<(int)reorderNodes.size(); offset+= diff, rcnt++) { + int blocks = (std::min(reorderNodes.size() - offset,diff)-1)/FvnBlockSize + 1; + + FastVorticityKernelNoshare<<<blocks,FvnBlockSize>>> + (reorderNodes.data(), reorderSourcePos.data(), reorderStrength.data(), srcCellStart.data(), srcPerCell.data(), + reg2, cutoff2, safeRadius2, cutoffInCells, make_int3(gridRes.x, gridRes.y, gridRes.z), maxres, reorderNodes.size(), FastEvalGridDivider, offset); + } + cout << "maxblocks " << MAXBLOCK << " total calls " << rcnt << endl; + + // download and reorder + reorderNodes.download(); + { + vector<int> curNodeCell(numCells); // redo ordering + for (int i=0; i<mesh.numNodes(); i++) { + int cell = cIndex(mesh.nodes(i).pos, gridRes); + if (cell < 0) continue; + int idx = nodesCellStart[cell] + curNodeCell[cell]; + if (!mesh.isNodeFixed(i)) + mesh.nodes(i).pos = reorderNodes[idx]; + curNodeCell[cell]++; + } + } +} + +#define MAXFRONT 500 + +__global__ void GaussKernel(CVec3Ptr distance, int* con, CVec3Ptr vort, CVec3Ptr vortSmooth, int blocksize, int stride, float cutoff, float mult, int offset) { + const int Cidx = threadIdx.x + blocksize * blockIdx.x + offset; + if (Cidx >= stride) return; + + int kernelIdx[MAXFRONT]; + float kernelDist[MAXFRONT]; + for(int i=0; i<MAXFRONT; i++) { + kernelIdx[i] = -1; + kernelDist[i] = 0; + } + int kernelElements = 1, newKernelElements = 1, lastPos = 0; + kernelIdx[0] = Cidx; + + //float a =0; + int iter; + for(iter=0; iter < 40; iter++) { + for (int i=lastPos; i<kernelElements; i++) { + + int curidx = kernelIdx[i]; + float curdist = kernelDist[i]; + + // add adjacent triangles to front list + float nextdist[3]; + nextdist[0] = distance.x[curidx]; + nextdist[1] = distance.y[curidx]; + nextdist[2] = distance.z[curidx]; + for (int c=0; c<3; c++) { + int nextidx = con[c+3*curidx]; + if (nextidx < 0) continue; + float dist = nextdist[c] + curdist; + if (dist > cutoff) continue; + + // check if already in list + bool found = false; + for (int j=0; j<newKernelElements; j++) { + if (kernelIdx[j] == nextidx) { + found = true; + if (kernelDist[j] > dist) kernelDist[j] = dist; + break; + } + } + if (!found) { + if (newKernelElements >= MAXFRONT-1) goto finish; + kernelIdx[newKernelElements] = nextidx; + kernelDist[newKernelElements] = dist; + newKernelElements++; + } + } + } + if (kernelElements == newKernelElements) break; + lastPos = kernelElements; + kernelElements = newKernelElements; + } + finish: + + // run gauss kernel over all nodes + float sum = 0; + float3 smooth = make_float3(0,0,0); + for (int j=0; j<newKernelElements; j++) { + const int idx = kernelIdx[j]; + float dist = kernelDist[j]; + float coeff = exp(dist*dist*mult); + sum += coeff; + smooth += make_float3(vort.x[idx] * coeff, vort.y[idx] * coeff, vort.z[idx] * coeff); + } + vortSmooth.set(Cidx, make_float3(smooth.x/sum, smooth.y/sum, smooth.z/sum)); +} + +PYTHON void filterVorticityCuda(VortexSheetMesh& mesh, Real sigma) { + const int len = mesh.numTris(); + + // upload mesh properties + CArray<int> connectivity(len*3); + CVec3Array faceCenter(len), distance(len), vorticity(len), vortSmooth(len); + for (int i=0; i<len; i++) { + faceCenter.set(i, mesh.getFaceCenter(i)); + vorticity.set(i, mesh.sheet(i).vorticity); + } + faceCenter.upload(); + vorticity.upload(); + vortSmooth.upload(); + for (int i=0; i<len; i++) { + Vec3 dist; + for (int c=0; c<3; c++) { + int ot = mesh.corners(mesh.corners(i,c).opposite).tri; + connectivity[i*3+c] = ot; + dist[c] = norm(faceCenter[i] - faceCenter[ot]); + } + distance.set(i, dist); + } + distance.upload(); + connectivity.upload(); + + const float cutoff = 2.0*sigma; + const float mult = -0.5/sigma/sigma; + + // invoke kernel in blocks (to avoid GUI stall) + const int blockSize = 128; + const int numblocks = 100; + const int maxProcess = blockSize * numblocks; + int cnt=0; + for (int offset=0; offset<len; offset+= maxProcess, cnt++) { + int blocks = (min(len - offset, maxProcess)-1)/blockSize + 1; + + GaussKernel<<<blocks,blockSize>>>(distance.data(), connectivity.data(), vorticity.data(), vortSmooth.data(), blockSize, len, cutoff, mult, offset); + } + cout << "Maxblocks: " << numblocks << ", Totalblocks: " << faceCenter.size()/blockSize << " -> " << cnt << " calls in total" << endl; + + // download and set + vortSmooth.download(); + for (int i=0; i<len; i++) { + mesh.sheet(i).vorticitySmoothed = vortSmooth[i]; + } +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/cudatools.h b/source/blender/python/manta_full/source/cuda/cudatools.h new file mode 100644 index 00000000000..c9435659ad5 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/cudatools.h @@ -0,0 +1,176 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Some helper functions for using CUDA + * + ******************************************************************************/ + +#ifndef _CUDATOOLS_H +#define _CUDATOOLS_H + +#include <cuda.h> +#include <vector> +#include <thrust/device_vector.h> +#include "vectorbase.h" + +// ***************************************************************** +// extending float3 type + +__device__ inline float3 operator+(const float3 &a, const float3 &b) { + return make_float3(a.x+b.x, a.y+b.y, a.z+b.z); +} +__device__ inline float3 operator*(const float3 &a, const float b) { + return make_float3(a.x*b, a.y*b, a.z*b); +} +__device__ inline float3 operator/(const float3 &a, const float b) { + return make_float3(a.x/b, a.y/b, a.z/b); +} +__device__ inline float3 operator*(const float b, const float3 &a) { + return make_float3(a.x*b, a.y*b, a.z*b); +} +__device__ inline float3 operator-(const float3 &a, const float3 &b) { + return make_float3(a.x-b.x, a.y-b.y, a.z-b.z); +} +__device__ inline void operator+=(float3 &a, const float3 &b) { + a.x += b.x; a.y += b.y; a.z += b.z; +} +__device__ inline float normSqr(const float3& v) { + return (v.x*v.x)+(v.y*v.y)+(v.z*v.z); +} +__device__ inline float norm(const float3& v) { + return sqrtf((v.x*v.x)+(v.y*v.y)+(v.z*v.z)); +} +__device__ inline float normalize(float3& v) { + float n = norm(v); + if (n==0) + v = make_float3(0,0,0); + else { + float in = 1.0f/n; + v = make_float3(v.x * in, v.y * in, v.z * in); + } + return n; +} +__device__ inline float3 cross(const float3& a, const float3& b) { + return make_float3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); +} +__device__ inline float dot(const float3&a, const float3& b) { + return (a.x*b.x + a.y*b.y + a.z*b.z); +} + + +namespace Manta { + +// ***************************************************************** +// some general macros + +inline void cudaAssert(cudaError_t er) { + if (er != cudaSuccess) { + throw Error("CUDA assert error: " + std::string(cudaGetErrorString(er))); + } +} +// ***************************************************************** +// define some helpers for unpacking Vec3s etc. + +//! Device object to access data provided by Vec3AArray +struct CVec3Ptr { + float *x, *y, *z; + __device__ inline float3 get(int i) const { return make_float3(x[i],y[i],z[i]); }; + __device__ inline void set(int i, const float3& v) { x[i]=v.x; y[i]=v.y; z[i]=v.z; }; +}; + +//! Provide host and data for easy coalescing by the GPU. +//! Has the same methods as CVector +struct CVec3Array { + + CVec3Array(int sz) { + x.resize(sz); + y.resize(sz); + z.resize(sz); + } + CVec3Array(const std::vector<Vec3>& v) { + x.resize(v.size()); + y.resize(v.size()); + z.resize(v.size()); + for (size_t i=0; i<v.size(); i++) { + x[i] = v[i].x; + y[i] = v[i].y; + z[i] = v[i].z; + } + upload(); + } + void upload() { + dx = x; + dy = y; + dz = z; + } + void download() { + x = dx; + y = dy; + z = dz; + } + void downloadTo(std::vector<Vec3>& v) { + download(); + if (v.size() != x.size()) + v.resize(x.size()); + for (size_t i=0; i<v.size(); i++) + v[i] = Vec3(x[i],y[i],z[i]); + } + + + CVec3Ptr data() { + CVec3Ptr a = { thrust::raw_pointer_cast(dx.data()), thrust::raw_pointer_cast(dy.data()), thrust::raw_pointer_cast(dz.data()) }; + return a; + } + inline const Vec3 operator[](int idx) const { return Vec3((Real)x[idx], (Real)y[idx], (Real)z[idx]); } + inline void set(int idx, const Vec3& v) { x[idx] = v.x; y[idx] = v.y; z[idx] = v.z; } + + inline int size() { return x.size(); } + inline int blocks(int blockSize) { return (x.size() - 1) / blockSize + 1; } + + thrust::host_vector<float> x, y, z; + thrust::device_vector<float> dx, dy, dz; +}; + +//! wrapper around thrust device vector for easier access in CUDA +template<class T> +struct CArray { + CArray(const std::vector<T>& v) : + dev(v.begin(), v.end()) + { + } + CArray(int sz) { + dev.resize(sz); + host.resize(sz); + } + T* data() { + return thrust::raw_pointer_cast(dev.data()); + } + void upload() { + if (host.size() == dev.size()) + dev = host; + } + void download() { + host = dev; + } + void downloadTo(std::vector<T>& v) { + thrust::copy(dev.begin(), dev.end(), v.begin()); + } + inline T& operator[](int idx) { return host[idx]; } + inline void set(int idx, T val) { host[idx] = val; } + + int size() { return dev.size(); } + int blocks(int blockSize) { return (dev.size()-1) / blockSize + 1; } + + thrust::host_vector<T> host; + thrust::device_vector<T> dev; +}; + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/curlnoise.cu b/source/blender/python/manta_full/source/cuda/curlnoise.cu new file mode 100644 index 00000000000..3f2b34dc0a6 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/curlnoise.cu @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * CUDA curl noise evaluation + * + ******************************************************************************/ + +#include "curlnoise.h" +#include "cudatools.h" +#include "noisefield.h" +#include "manta.h" + +using namespace std; + +namespace Manta { + + +void CudaNoiseTexture::init() { + cudaExtent noiseSize = {NOISE_SIZE, NOISE_SIZE, NOISE_SIZE}; + size_t memSize = noiseSize.width*noiseSize.height*noiseSize.depth; + + // register channels + cudaChannelFormatDesc noiseChannelDesc = cudaCreateChannelDesc(16, 16, 16, 16, cudaChannelFormatKindSigned); + cudaAssert(cudaMalloc3DArray(&mNoiseArray, &noiseChannelDesc, noiseSize)); + + // create noise objects + WaveletNoiseField nx(NULL), ny(NULL), nz(NULL); + + // find scales + float maxval = -1e20f; + for (size_t i = 0; i < memSize; i++) + maxval = std::max(std::max(std::max(fabs(nx.data()[i]), fabs(ny.data()[i])), fabs(nz.data()[i])), maxval); + + float scale = 65535.0f / (2.0 * maxval); + mScale = maxval; + + // alloc local buffer + short4* buffer = new short4[memSize]; + for (size_t i = 0; i < memSize; i++) { + buffer[i].x = (signed short) (nx.data()[i] * scale); + buffer[i].y = (signed short) (ny.data()[i] * scale); + buffer[i].z = (signed short) (nz.data()[i] * scale); + buffer[i].w = 0; + } + + // copy to page-locked mem + cudaPitchedPtr page_locked_ptr; + page_locked_ptr.pitch = noiseSize.width*sizeof(short4); + page_locked_ptr.xsize = noiseSize.width; + page_locked_ptr.ysize = noiseSize.height; + page_locked_ptr.ptr = buffer; + + // copy data to 3D array + cudaMemcpy3DParms copy_params = {0}; + copy_params.srcPtr = page_locked_ptr; + copy_params.dstArray = mNoiseArray; + copy_params.extent = noiseSize; + copy_params.kind = cudaMemcpyHostToDevice; + cudaAssert(cudaMemcpy3D(©_params)); + + //FILE* fp=fopen("/tmp/a.bin","wb"); fwrite(nx.data(), sizeof(float), 128*128*128, fp); fclose(fp); + delete[] buffer; +} + +CudaNoiseDev CudaNoiseTexture::bind(CompressedTex& tex) { + // Bind the texture + tex.normalized = true; + tex.filterMode = cudaFilterModeLinear; // this kills high frequencies ! + tex.addressMode[0] = cudaAddressModeWrap; + tex.addressMode[1] = cudaAddressModeWrap; + tex.addressMode[2] = cudaAddressModeWrap; + cudaAssert(cudaBindTextureToArray(tex, mNoiseArray)); + + return CudaNoiseDev(mScale); +} + +CudaNoiseTexture::~CudaNoiseTexture() { + cudaFreeArray(mNoiseArray); +} + +/* +CompressedTex noise; + +__global__ void rewriteNoise(CudaNoiseDev nd, float* a) { + int3 cell = make_int3(threadIdx.x + blockDim.x*blockIdx.x, threadIdx.y + blockDim.y*blockIdx.y, threadIdx.z + blockDim.z*blockIdx.z); + float scale = 1.0f/128.0f; + float3 p = make_float3((float)(cell.x+0.5f) * scale, (float)(cell.y+0.5f) * scale, (float)(cell.z+0.5f) * scale); + float4 n = tex3D(noise, p.x, p.y, p.z); + a[cell.x + 128*cell.y + 128*128*cell.z] = (float)n.x * nd.scale; +} + +PYTHON void testme() { + CudaNoiseTexture nd; + CArray<float> a(128*128*128); + a.upload(); + + dim3 blocksize(8, 8, 8); + dim3 blocks(128/8, 128/8, 128/8); + rewriteNoise<<<blocks, blocksize>>>(nd.bind(noise), a.data()); + a.download(); + FILE* fp=fopen("/tmp/re.bin","wb"); fwrite(&a[0], sizeof(float), 128*128*128, fp); fclose(fp); +}*/ + +} //namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/curlnoise.h b/source/blender/python/manta_full/source/cuda/curlnoise.h new file mode 100644 index 00000000000..121e47291f7 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/curlnoise.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * CUDA curl noise evaluation + * + ******************************************************************************/ + +#ifndef _CURLNOISE_H +#define _CURLNOISE_H + +#include "cudatools.h" + +namespace Manta { + +const int NOISE_SIZE = 128; +typedef texture<short4, 3, cudaReadModeNormalizedFloat> CompressedTex; + +//! Device interface for CUDA noise texture +struct CudaNoiseDev { + CudaNoiseDev(float s) : scale(s) {}; + float scale; + + __device__ inline float3 eval(CompressedTex tex, const float3& p); + __device__ inline float3 synthesizeK41(CompressedTex tex, const float3& p, int octaves, float L0); +}; + +//! Host interface for CUDA noise texture +class CudaNoiseTexture { +public: + CudaNoiseTexture() { init(); } + ~CudaNoiseTexture(); + void init(); + CudaNoiseDev bind(CompressedTex& tex); + +//private: + cudaArray* mNoiseArray; + float mScale; +}; + + + + +// ***************************************************************************** +// Implementation +// ***************************************************************************** + +template<char X, int N, int M> +__device__ inline float derivativeWeight(const float t) +{ + if ((X == 'x' && N == 0) || (X == 'y' && N == 1) || (X == 'z' && N == 2)) { + if (M == 0) + return t; + else if (M == 1) + return 1.0f - 2.0f * t; + else + return t - 1.0f; + } else { + if (M == 0) + return t * t * 0.5f; + else if (M == 1) + return 0.5f + t - t*t; + else + return 0.5f - t + 0.5f * t * t; + } +} + +__device__ inline float3 CudaNoiseDev::eval(CompressedTex noise, const float3& p) { + float cache[12]; + float ext = 1.0f / (float) NOISE_SIZE; + + float t0, t1, t2; + float midX, midY, midZ; + + midX = ceil(p.x - 0.5f); + t0 = midX - (p.x - 0.5f); + midX -= 1.0f; + midX *= ext; + + midY = ceil(p.y - 0.5f); + t1 = midY - (p.y - 0.5f); + midY -= 1.0f; + midY *= ext; + + midZ = ceil(p.z - 0.5f); + t2 = midZ - (p.z - 0.5f); + midZ -= 1.0f; + midZ *= ext; + + cache[0] = derivativeWeight<'x', 2, 0>(t2); + cache[1] = derivativeWeight<'x', 2, 1>(t2); + cache[2] = derivativeWeight<'x', 2, 2>(t2); + + cache[3] = derivativeWeight<'z', 2, 0>(t2); + cache[4] = derivativeWeight<'z', 2, 1>(t2); + cache[5] = derivativeWeight<'z', 2, 2>(t2); + + cache[6] = derivativeWeight<'x', 1, 0>(t1); + cache[7] = derivativeWeight<'x', 1, 1>(t1); + cache[8] = derivativeWeight<'x', 1, 2>(t1); + + cache[9] = derivativeWeight<'y', 1, 0>(t1); + cache[10] = derivativeWeight<'y', 1, 1>(t1); + cache[11] = derivativeWeight<'y', 1, 2>(t1); + + float3 v; + v.x = 0.0f; + v.y = 0.0f; + v.z = 0.0f; + + /////////////////////////////////////////////////////////////////////////////////////// + // x, y, z derivatives + /////////////////////////////////////////////////////////////////////////////////////// + for (int k = 0; k < 3; k++, midZ += ext) + { + for (int j = 0; j < 3; j++, midY += ext) + { + for (int i = 0; i < 3; i++, midX += ext) + { + // Read the noise texture + float4 n = tex3D(noise, midX, midY, midZ); + + float w2_x = cache[0 + k]; + float w2_y = w2_x; + float w2_z = cache[3 + k]; + + float w1_x = cache[6 + j]; + float w1_y = cache[9 + j]; + float w1_z = w1_x; + + float w0_x = (i == 0) ? derivativeWeight<'x', 0, 0>(t0) : + (i == 1) ? derivativeWeight<'x', 0, 1>(t0) : + derivativeWeight<'x', 0, 2>(t0); + + float w0_y = (i == 0) ? derivativeWeight<'y', 0, 0>(t0) : + (i == 1) ? derivativeWeight<'y', 0, 1>(t0) : + derivativeWeight<'y', 0, 2>(t0); + float w0_z = w0_y; + + // Decompress noise + n.x *= scale; + n.y *= scale; + n.z *= scale; + + // Calculate the final weights + float w_x = w0_x * w1_x * w2_x; + float w_y = w0_y * w1_y * w2_y; + float w_z = w0_z * w1_z * w2_z; + + // Add the weighted noise + v.z += n.y * w_x; + v.y -= n.z * w_x; + + v.z -= n.x * w_y; + v.x += n.z * w_y; + + v.y += n.x * w_z; + v.x -= n.y * w_z; + } + midX -= 3.0f * ext; + } + midY -= 3.0f * ext; + } + + return v; +} + +const __device__ float PERSISTENCE = 0.56123f; + +__device__ inline float3 CudaNoiseDev::synthesizeK41(CompressedTex tex, const float3& p, int octaves, float L0) { + // Base parameters for octave 0 + float multiplier = L0; + float amplitude = 1.0f; + float3 vel = make_float3(0,0,0); + + for (int octave = 0; octave < octaves; octave++) { + float3 noiseLookup = eval(tex, multiplier * p); + vel += noiseLookup * amplitude; + + // next scale + amplitude *= PERSISTENCE; + multiplier *= 2.0f; + } + return vel; +} + + + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/cuda/meshtools.cu b/source/blender/python/manta_full/source/cuda/meshtools.cu new file mode 100644 index 00000000000..4ad4232fad5 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/meshtools.cu @@ -0,0 +1,165 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GPU SDF creation from triangle mesh + * + ******************************************************************************/ + +#include "cudatools.h" +#include "mesh.h" +#include "grid.h" +#include <stack> + +using namespace std; +namespace Manta { + +const int SDFBlockSize = 8; + +__global__ void SDFKernel(const int* partStart, const int* partLen, CVec3Ptr pos, CVec3Ptr normal, float* sdf, int3 gridRes, int intRadius, float safeRadius2, float cutoff2, float isigma2) +{ + // cell index, center + int3 cell = make_int3(threadIdx.x + blockDim.x*blockIdx.x, threadIdx.y + blockDim.y*blockIdx.y, threadIdx.z + blockDim.z*blockIdx.z); + if (cell.x >= gridRes.x || cell.y >= gridRes.y || cell.z >= gridRes.z) return; + float3 cpos = make_float3(cell.x + 0.5f, cell.y + 0.5f, cell.z + 0.5f); + float sum = 0.0f; + float dist = 0.0f; + + // query cells within block radius + int3 minBlock = make_int3(max(cell.x - intRadius,0), max(cell.y - intRadius,0), max(cell.z - intRadius,0)); + int3 maxBlock = make_int3(min(cell.x + intRadius, gridRes.x - 1), min(cell.y + intRadius, gridRes.y - 1), min(cell.z + intRadius, gridRes.z - 1)); + for (int i=minBlock.x; i<=maxBlock.x; i++) + for (int j=minBlock.y; j<=maxBlock.y; j++) + for (int k=minBlock.z; k<=maxBlock.z; k++) { + // test if block is within radius + float3 d = make_float3(cell.x-i, cell.y-j, cell.z-k); + if (normSqr(d) > safeRadius2) continue; + + // find source cell, and divide it into thread blocks + int block = i + gridRes.x * (j + gridRes.y * k); + int slen = partLen[block]; + if (slen == 0) continue; + int start = partStart[block]; + + // process sources + for(int s=0; s<slen; s++) { + + // actual sdf kernel + float3 r = cpos - pos.get(start+s); + float r2 = normSqr(r); + if (r2 < cutoff2) { + float w = expf(-r2*isigma2); + sum += w; + dist += dot(normal.get(start+s), r) * w; + } + } + } + + // writeback + if (sum > 0.0f) + sdf[cell.x + gridRes.x * (cell.y + gridRes.y * cell.z)] = dist / sum; +} + +inline int _cIndex(const Vec3& pos, const Vec3i& s) { + Vec3i p = toVec3i(pos); + if (p.x < 0 || p.y < 0 || p.z < 0 || p.x >= s.x || p.y >= s.y || p.z >= s.z) return -1; + return p.x + s.x * (p.y + s.y * p.z); +} + +//! Obtain levelset from mesh. +//! This only works for dense meshes -- simply uses normals and triangle centers, no triangle integration +PYTHON void meshSDFCuda(Mesh& mesh, Grid<Real>& levelset, Real sigma, Real cutoff=-1) +{ + if (cutoff<0) cutoff = 2*sigma; + + Vec3i gridRes = levelset.getSize(); + Vec3 mult = toVec3(gridRes) / toVec3(mesh.getParent()->getGridSize()); + + // prepare center values + vector<Vec3> center(mesh.numTris()); + for(size_t i=0; i<mesh.numTris(); i++) + center[i] = mesh.getFaceCenter(i) * mult; + + // prepare grid + const int numCells = gridRes.x * gridRes.y * gridRes.z; + CArray<Real> gridDev(numCells); + for (int i=0; i<numCells; i++) + gridDev[i] = -cutoff; + gridDev.upload(); + + // 1. count sources per cell + CArray<int> srcPerCell(numCells); + for (size_t i=0; i<center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell >= 0) + srcPerCell[cell]++; + } + srcPerCell.upload(); + + // 2. create start index lookup + CArray<int> srcCellStart(numCells); + int cnt=0; + for (int i=0; i<numCells; i++) { + srcCellStart[i] = cnt; + cnt += srcPerCell[i]; + } + srcCellStart.upload(); + + // 3. reorder nodes + CVec3Array reorderPos(center.size()); + CVec3Array reorderNormal(center.size()); + { + vector<int> curSrcCell(numCells); + for (int i=0; i<(int)center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell < 0) continue; + int idx = srcCellStart[cell] + curSrcCell[cell]; + reorderPos.set(idx, center[i]); + reorderNormal.set(idx, mesh.getFaceNormal(i)); + curSrcCell[cell]++; + } + } + reorderPos.upload(); + reorderNormal.upload(); + + // construct parameters + float safeRadius = cutoff + sqrt(3.0)*0.5; + float safeRadius2 = safeRadius*safeRadius; + float cutoff2 = cutoff*cutoff; + float isigma2 = 1.0/(sigma*sigma); + int intRadius = (int)(cutoff+0.5); + + dim3 blocksize(SDFBlockSize, SDFBlockSize, SDFBlockSize); + dim3 blocks((gridRes.x-1)/SDFBlockSize+1, (gridRes.y-1)/SDFBlockSize+1, (gridRes.z-1)/SDFBlockSize+1); + SDFKernel<<<blocks, blocksize>>>(srcCellStart.data(), srcPerCell.data(), reorderPos.data(), reorderNormal.data(), gridDev.data(), + make_int3(gridRes.x, gridRes.y, gridRes.z), intRadius, safeRadius2, cutoff2, isigma2); + + gridDev.download(); + for (int i=0;i<numCells; i++) + levelset[i] = gridDev[i]; + + // floodfill outside + stack<Vec3i> outside; + FOR_IJK(levelset) { + if (levelset(i,j,k) >= cutoff-1.0f) + outside.push(Vec3i(i,j,k)); + } + while(!outside.empty()) { + Vec3i c = outside.top(); + outside.pop(); + levelset(c) = cutoff; + if (c.x > 0 && levelset(c.x-1, c.y, c.z) < 0) outside.push(Vec3i(c.x-1,c.y,c.z)); + if (c.y > 0 && levelset(c.x, c.y-1, c.z) < 0) outside.push(Vec3i(c.x,c.y-1,c.z)); + if (c.z > 0 && levelset(c.x, c.y, c.z-1) < 0) outside.push(Vec3i(c.x,c.y,c.z-1)); + if (c.x < levelset.getSizeX()-1 && levelset(c.x+1, c.y, c.z) < 0) outside.push(Vec3i(c.x+1,c.y,c.z)); + if (c.y < levelset.getSizeY()-1 && levelset(c.x, c.y+1, c.z) < 0) outside.push(Vec3i(c.x,c.y+1,c.z)); + if (c.z < levelset.getSizeZ()-1 && levelset(c.x, c.y, c.z+1) < 0) outside.push(Vec3i(c.x,c.y,c.z+1)); + }; +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/particle.cu b/source/blender/python/manta_full/source/cuda/particle.cu new file mode 100644 index 00000000000..bc1ad3ee85d --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/particle.cu @@ -0,0 +1,172 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GPU SDF creation from triangle mesh + * + ******************************************************************************/ + +#include "cudatools.h" +#include "particle.h" +#include "grid.h" + +/*using namespace std; +namespace Manta { +using namespace std; +using DDF::nVec3i; +using DDF::Vec3; + +// vortex particle effect: (cyl coord around wp) +// u = -|wp|*rho*exp( (-rho^2-z^2)/(2sigma^2) ) e_phi +__device__ inline float3 velocityKernel(const float3& r, const float3& vortNorm, const float strength, const float isigma) { + // transform in cylinder coordinate system + const float rlen2 = normSqr(r); + const float rlendiv = rsqrtf(rlen2); + const float z = dot(r, vortNorm); + const float3 ePhi = cross(r, vortNorm) * rlendiv; + const float rho2 = rlen2 - z*z; + + float vortex; + if (rho2 > 1e-10) { + // evaluate Kernel + vortex = strength * sqrtf(rho2) * expf (rlen2 * isigma); + } else { + vortex = 0; + } + return vortex * ePhi; +} + +__device__ inline float3 rk4(const float3& p, const float3& vortNorm, const float strength, const float isigma, const float dt) { + float3 k1 = velocityKernel(p, vortNorm, strength, isigma); + float3 p2 = p + k1 * (0.5*dt); + float3 k2 = velocityKernel(p2, vortNorm, strength, isigma); + float3 p3 = p + k2 * (0.5*dt); + float3 k3 = velocityKernel(p3, vortNorm, strength, isigma); + float3 p4 = p + dt*k3; + float3 k4 = velocityKernel(p4, vortNorm, strength, isigma); + return (k1 + (k2+k3)*2.0 + k4) * (dt/6.0); +} + +const int VortKernelBlockSize = 256; + +// apply vortex kernel to nodes +__global__ void integrateNodesRK4 (AlignedVec3Array nodes, const AlignedVec3Array sources, const AlignedVec3Array vortex, const AlignedFloatArray sigma2, + const float dt) +{ + const int globalIdx = threadIdx.x + VortKernelBlockSize * blockIdx.x; + const int localIdx = threadIdx.x; + __shared__ float3 vpos[VortKernelBlockSize]; + __shared__ float3 vort[VortKernelBlockSize]; + __shared__ float vstr[VortKernelBlockSize]; + __shared__ float vsig[VortKernelBlockSize]; + + // load current position + float3 pos = nodes.get(globalIdx); + float3 u = pos; + + // divide sources into blocks for shared memeory usage + for (int i=0; i<sources.blocks; i++) { + const int blockIdx = VortKernelBlockSize * i; + const int srcGlobal = localIdx + blockIdx; + + // load shared data + vpos[localIdx] = sources.get(srcGlobal); + vsig[localIdx] = sigma2.get(srcGlobal); + float3 v = vortex.get(srcGlobal); + float vnorm = normalize(v); + vstr[localIdx] = vnorm; + vort[localIdx] = v; + __syncthreads(); + + if (globalIdx < nodes.len) { + for (int j=0; j<VortKernelBlockSize; j++) { + if (j+blockIdx >= sources.len) continue; + + // apply actual vorticity kernel + float3 r = pos - vpos[j]; + float sig2 = vsig[j]; + float r2 = normSqr(r); + float cutoff2 = 6.0f*sig2; + if (r2 > cutoff2 || r2 < 1e-8) continue; + // RK4 integration + u += rk4(r, vort[j], vstr[j], -0.5/sig2, dt); + } + } + __syncthreads(); + } + + // writeback + if (globalIdx < nodes.len) + nodes.set(globalIdx, u); +} + +void cudaIntegrateVortexParticles(vector<SurfaceNode>& nodes, DDF::ParticleSystemVortex& sys, const float scale, const float dt, const float dx) +{ + // count valid source, create host array + int numSources = 0; + for(int i=0; i<sys.size(); i++) + if (sys.isActive(i)) numSources++; + + vector<Vec3> srcPos(numSources), srcStr(numSources); + vector<float> srcSig(numSources); + for (int i=0,idx=0; i<sys.size(); i++) { + if (sys.isActive(i)) { + srcPos[idx] = sys.pos(i); + srcStr[idx] = sys.vortex(i).vorticity * scale; + float sig = sys.vortex(i).sigma; + srcSig[idx] = sig*sig; + idx++; + } + } + if (numSources == 0) return; + + // count valid dest, create host arrays + int numDest = 0; + for(size_t i=0; i<nodes.size(); i++) + if ((nodes[i].flags & SurfaceNode::FIXED) == 0) numDest++; + + vector<Vec3> dstPos(numDest); + for(int i=0,idx=0; i<(int)nodes.size(); i++) { + if ((nodes[i].flags & SurfaceNode::FIXED) == 0) { + SmVector3 p = nodes[i].pos; + dstPos[idx] = Vec3(p[0] / dx, p[1] / dx, p[2] / dx); + idx++; + } + } + + // upload sources to GPU + DeviceVec3 dSrcPos(VortKernelBlockSize), dSrcStr(VortKernelBlockSize), dDstPos(VortKernelBlockSize), dVDstPos(VortKernelBlockSize); + DeviceFloat dSrcSig(VortKernelBlockSize); + dSrcPos.upload(srcPos); + dSrcSig.upload(srcSig); + dSrcStr.upload(srcStr); + + // apply to mesh + dDstPos.upload(dstPos); + integrateNodesRK4<<<dDstPos.blocks(), VortKernelBlockSize>>>(dDstPos.device, dSrcPos.device, dSrcStr.device, dSrcSig.device, dt); + dDstPos.errorTest(); + dDstPos.download(dstPos); + + // apply to vortex particles + dVDstPos.upload(srcPos); + integrateNodesRK4<<<dVDstPos.blocks(), VortKernelBlockSize>>>(dVDstPos.device, dSrcPos.device, dSrcStr.device, dSrcSig.device, dt); + dVDstPos.errorTest(); + dVDstPos.download(srcPos); + + // back to arrays... + for (int i=0,idx=0; i<sys.size(); i++) + if (sys.isActive(i)) sys.setPos(i, srcPos[idx++]); + + for (int i=0,idx=0; i<(int)nodes.size(); i++) { + if ((nodes[i].flags & SurfaceNode::FIXED) == 0) { + nodes[i].pos = SmVector3(dstPos[idx].x * dx, dstPos[idx].y * dx, dstPos[idx].z * dx); + idx++; + } + } +} +*/
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/cuda/turbulence.cu b/source/blender/python/manta_full/source/cuda/turbulence.cu new file mode 100644 index 00000000000..2ea9847cac0 --- /dev/null +++ b/source/blender/python/manta_full/source/cuda/turbulence.cu @@ -0,0 +1,164 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GPU turbulence synthesis + * + ******************************************************************************/ + +#include "curlnoise.h" +#include "vortexsheet.h" +#include "grid.h" +#include "commonkernels.h" + +using namespace std; +namespace Manta { + +CompressedTex gNoiseTex; + +//! synthesize uniform K41 curl noise onto node list +__global__ void KnSynthesizeK41(CVec3Ptr nodes, CVec3Ptr tex1, CVec3Ptr tex2, Real alpha, float* ke, + CudaNoiseDev noise, const int octaves, const float iL0, const float str, const int len, const float kmin) +{ + const int nodeIdx = threadIdx.x + blockDim.x * blockIdx.x; + if (nodeIdx >= len) return; + + // load from array + float3 p = nodes.get(nodeIdx); + float3 tc1 = tex1.get(nodeIdx); + float3 tc2 = tex2.get(nodeIdx); + float k = ke ? ke[nodeIdx] : 1.0f; + //tc1=p; // static + k = k - kmin; + k = (k<0.0f) ? 0.0f : sqrtf(k); + + // interpolate texcoords + /*float3 tc = alpha * tc1 + (1.0f-alpha) * tc2; + float3 v = noise.synthesizeK41(gNoiseTex, tc, octaves, L0);*/ + + // interpolate velocities + float3 v1 = noise.synthesizeK41(gNoiseTex, tc1, octaves, iL0); + float3 v2 = noise.synthesizeK41(gNoiseTex, tc2, octaves, iL0); + float3 v = alpha * v1 + (1.0f-alpha) * v2; + + // apply + float3 update = (str * k) * v; + p += update; + tc1 += update; + tc2 += update; + + // writeback + nodes.set(nodeIdx, p); + tex1.set(nodeIdx, tc1); + tex2.set(nodeIdx, tc2); +} + +//! synthesize K41 curl noise onto mesh +PYTHON void synthesizeK41(VortexSheetMesh& mesh, Grid<Real>* k = NULL, + Real scale = 1.0, Real L0 = 0.1, int octaves=3, Real switchLength = 10.0, bool hardReset=false, Real minIntensity=0.1) +{ + const int blockSize = 256; + const int blocks = (mesh.numNodes()-1)/blockSize+1; + const float dt = parent->getDt(); + const float str = dt * scale; + const float kmin = 1.5 * square(minIntensity); + + // hat function over time + static float ctime = 0; + float oldAlpha = 2.0f*nmod(ctime/switchLength, 1.0f); + ctime += parent->getDt(); + float alpha = 2.0f*nmod(ctime/switchLength, 1.0f); + if (hardReset) { + if (oldAlpha > alpha) mesh.resetTex1(); + alpha = 1.0f; + } else { + if (oldAlpha < 1.0f && alpha >= 1.0f) mesh.resetTex2(); + if (oldAlpha > alpha) mesh.resetTex1(); + if (alpha>1.0f) alpha=2.0f-alpha; + } + + // create noise tex on first call + static CudaNoiseTexture noise; + + // upload data + CVec3Array nodes(mesh.numNodes()); + CVec3Array tc1(mesh.numNodes()), tc2(mesh.numNodes()); + CArray<float> ke(mesh.numNodes()); + for (int i=0; i<mesh.numNodes(); i++) { + nodes.set(i, mesh.nodes(i).pos); + tc1.set(i, mesh.tex1(i)); + tc2.set(i, mesh.tex2(i)); + } + nodes.upload(); + tc1.upload(); + tc2.upload(); + if (k) { + for (int i=0; i<mesh.numNodes(); i++) + ke.set(i, k->getInterpolated(mesh.nodes(i).pos)); + ke.upload(); + } + KnSynthesizeK41<<<blocks, blockSize>>> (nodes.data(), tc1.data(), tc2.data(), alpha, k ? (ke.data()) : 0, + noise.bind(gNoiseTex), octaves, 1.0f/L0, str, mesh.numNodes(), kmin); + + // download data + nodes.download(); + tc1.download(); + tc2.download(); + for (int i=0; i<mesh.numNodes(); i++) { + if (!mesh.isNodeFixed(i)) { + mesh.nodes(i).pos = nodes[i]; + mesh.tex1(i) = tc1[i]; + mesh.tex2(i) = tc2[i]; + } + } +} + +//! Kernel: synthesize uniform K41 curl noise onto grid +__global__ void KnSynthesizeGridK41(CVec3Ptr nodes, CudaNoiseDev noise, const int octaves, const float iL0, const float str, int stridey, int stridez) +{ + int3 cell = make_int3(threadIdx.x + blockDim.x*blockIdx.x, threadIdx.y + blockDim.y*blockIdx.y, threadIdx.z + blockDim.z*blockIdx.z); + float3 p = make_float3(cell.x , cell.y , cell.z ); + + // interpolate velocities + float3 v = noise.synthesizeK41(gNoiseTex, p, octaves, iL0); + + // writeback + nodes.set(cell.x + stridey* cell.y + stridez* cell.z, str*v); +} + +//! synthesize K41 curl noise onto grid +PYTHON void synthesizeK41Grid(MACGrid& vel, MACGrid& dst, Real scale = 1.0, Real L0 = 0.1, int octaves=3) +{ + const float dt = parent->getDt(); + const float str = dt * scale; + + // create noise tex on first call + static CudaNoiseTexture noise; + + // upload data + const Vec3i gridRes = parent->getGridSize(); + const int num = vel.getSizeX()*vel.getSizeY()*vel.getSizeZ(); + CVec3Array nodes(num); + nodes.upload(); + + dim3 blocksize(8, 8, 8); + dim3 blocks((gridRes.x-1)/8+1, (gridRes.y-1)/8+1, (gridRes.z-1)/8+1); + KnSynthesizeGridK41<<<blocks, blocksize>>> (nodes.data(), noise.bind(gNoiseTex), octaves, 1.0f/L0, str, vel.getStrideY(), vel.getStrideZ()); + + nodes.download(); + Grid<Vec3> center(parent); + MACGrid mac(parent); + for (int i=0; i<num; i++) + center[i] = nodes[i]; + + GetMAC(mac, center); + dst = vel; + dst += (Grid<Vec3>&)mac; +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/edgecollapse.cpp b/source/blender/python/manta_full/source/edgecollapse.cpp new file mode 100644 index 00000000000..a2ef21d8f57 --- /dev/null +++ b/source/blender/python/manta_full/source/edgecollapse.cpp @@ -0,0 +1,666 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Mesh edge collapse and subdivision + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#include "edgecollapse.h" +#include <queue> + +using namespace std; + +namespace Manta { + +// 8-point butterfly subdivision scheme (as described by Brochu&Bridson 2009) +Vec3 ButterflySubdivision(Mesh& m, const Corner &ca, const Corner &cb) +{ + Vec3 p = m.nodes(m.corners(ca.prev).node).pos + m.nodes(m.corners(ca.next).node).pos; + Vec3 q = m.nodes(ca.node).pos + m.nodes(cb.node).pos; + Vec3 r = m.nodes(m.corners(m.corners(ca.next).opposite).node).pos + + m.nodes(m.corners(m.corners(ca.prev).opposite).node).pos + + m.nodes(m.corners(m.corners(cb.next).opposite).node).pos + + m.nodes(m.corners(m.corners(cb.prev).opposite).node).pos; + return ( 8*p + 2*q - r)/16.0; +} + +// Modified Butterfly Subdivision Scheme from: +// Interpolating Subdivision for Meshes with Arbitrary Topology +// Denis Zorin, Peter Schroder, and Wim Sweldens +// input the Corner that satisfies the following: +// c.prev.node is the extraordinary vertex, +// and c.next.node is the other vertex involved in the subdivision +Vec3 OneSidedButterflySubdivision(Mesh& m, const int valence, const Corner &c) { + Vec3 out; + Vec3 p0 = m.nodes(m.corners(c.prev).node).pos; + Vec3 p1 = m.nodes(m.corners(c.next).node).pos; + + if(valence==3) { + Vec3 p2 = m.nodes(c.node).pos; + Vec3 p3 = m.nodes(m.corners(m.corners(c.next).opposite).node).pos; + out = (5.0/12.0)*p1 - (1.0/12.0)*(p2+p3) + 0.75*p0; + } + else if(valence==4) { + Vec3 p2 = m.nodes(m.corners(m.corners(c.next).opposite).node).pos; + out = 0.375*p1 - 0.125*p2 + 0.75*p0; + } + else { + // rotate around extraordinary vertex, + // calculate subdivision weights, + // and interpolate vertex position + double rv = 1.0/(double)valence; + out = 0.0; + int current = c.prev; + for(int j=0; j<valence; j++) { + double s = (0.25 + cos(2*M_PI*j*rv) + 0.5*cos(4*M_PI*j*rv))*rv; + Vec3 p = m.nodes(m.corners(m.corners(current).prev).node).pos; + + out += s*p; + current = m.corners(m.corners(m.corners(current).next).opposite).next; + } + out += 0.75* m.nodes(m.corners(c.prev).node).pos; + } + return out; +} + +// Modified Butterfly Subdivision Scheme from: +// Interpolating Subdivision for Meshes with Arbitrary Topology +// Denis Zorin, Peter Schroder, and Wim Sweldens +Vec3 ModifiedButterflySubdivision(Mesh& m, const Corner &ca, const Corner &cb, const Vec3& fallback) +{ + // calculate the valence of the two parent vertices + int start = ca.prev; + int current = start; + int valenceA = 0; + do { + valenceA++; + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) return fallback; + current = m.corners(op).next; + } + while(current != start); + start = ca.next; + current = start; + int valenceB = 0; + do { + valenceB++; + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) return fallback; + current = m.corners(op).next; + } + while(current != start); + + // if both vertices have valence 6, use butterfly subdivision + if(valenceA==6 && valenceB==6) { + return ButterflySubdivision(m, ca,cb); + } + else if(valenceA==6) // use a one-sided scheme + { + return OneSidedButterflySubdivision(m, valenceB,cb); + } + else if(valenceB==6) // use a one-sided scheme + { + return OneSidedButterflySubdivision(m, valenceA,ca); + } + else // average the results from two one-sided schemes + { + return 0.5*( OneSidedButterflySubdivision(m, valenceA,ca) + + OneSidedButterflySubdivision(m, valenceB,cb) ); + } +} + +bool gAbort = false; + +// collapse an edge on triangle "trinum". +// "which" is 0,1, or 2, +// where which==0 is the triangle edge from p0 to p1, +// which==1 is the triangle edge from p1 to p2, +// and which==2 is the triangle edge from p2 to p0, +void CollapseEdge(Mesh& m, const int trinum, const int which, const Vec3 &edgevect, const Vec3 &endpoint, + vector<int> &deletedNodes, std::map<int,bool> &taintedTris, int &numCollapses, bool doTubeCutting) +{ + if (gAbort) return; + // I wanted to draw a pretty picture of an edge collapse, + // but I don't know how to make wacky angled lines in ASCII. + // Instead, I will show the before case and tell you what needs to be done. + + // BEFORE: + // * + // / \. + // /C0 \. + // / \. + // / \. + // / B \. + // / \. + // /C1 C2 \. + // P0 *---------------* P1 + // \C2 C1 / + // \ / + // \ A / + // \ / + // \ / + // \C0 / + // \ / + // * + // + // We are going to collapse the edge between P0 and P1 + // by deleting P1, + // and taking all references to P1, + // and rerouting them to P0 instead + // + // What we need to do: + // Move position of P0 + // Preserve connectivity in both triangles: + // (C1.opposite).opposite = C2.o + // (C2.opposite).opposite = C1.o + // Delete references to Corners of deleted triangles in both P0 and P1's Corner list + // Reassign references to P1: + // loop through P1 triangles: + // rename P1 references to P0 in p lists. + // rename Corner.v references + // Copy P1's list of Corners over to P0's list of Corners + // Delete P1 + + Corner ca_old[3], cb_old[3]; + ca_old[0] = m.corners(trinum, which); + ca_old[1] = m.corners(ca_old[0].next); + ca_old[2] = m.corners(ca_old[0].prev); + bool haveB = false; + if (ca_old[0].opposite>=0) { + cb_old[0] = m.corners(ca_old[0].opposite); + cb_old[1] = m.corners(cb_old[0].next); + cb_old[2] = m.corners(cb_old[0].prev); + haveB = true; + } + if (!haveB) { + // for now, don't collapse + return; + } + + int P0 = ca_old[2].node; + int P1 = ca_old[1].node; + + /////////////// + // avoid creating nonmanifold edges + bool nonmanifold = false; + bool nonmanifold2 = false; + + set<int>& ring0 = m.get1Ring(P0).nodes; + set<int>& ring1 = m.get1Ring(P1).nodes; + + // check for intersections of the 1-rings of P0,P1 + int cl=0, commonVert=-1; + for(set<int>::iterator it=ring1.begin(); it != ring1.end(); ++it) + if (ring0.find(*it) != ring0.end()) { + cl++; + if (*it != ca_old[0].node && *it != cb_old[0].node) commonVert = *it; + } + + nonmanifold = cl>2; + nonmanifold2 = cl>3; + + if(nonmanifold && + ca_old[1].opposite>=0 && cb_old[1].opposite>=0 && + ca_old[2].opposite>=0 && cb_old[2].opposite>=0 ) // collapsing this edge would create a non-manifold edge + { + if(nonmanifold2) + return; + + bool topTet = false; + bool botTet = false; + // check if collapsing this edge will collapse a tet. + if(m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node) + botTet = true; + + if(m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node) + topTet = true; + + if(topTet^botTet) { + + // safe pyramid case. + // collapse the whole tet! + // First collapse the top of the pyramid, + // then carry on collapsing the original verts. + Corner cc_old[3],cd_old[3]; + if(botTet) + cc_old[0] = m.corners(ca_old[1].opposite); + else // topTet + cc_old[0] = cb_old[2]; + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + for (int i=0; i<m.numTriChannels(); i++) + {};//TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + m.removeTriFromLookup(tmpTrinum); + m.removeTriFromLookup(tmpOthertri); + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + deletedNodes.push_back(P3); + + numCollapses++; + + // recompute Corners for triangles A and B + if(botTet) + ca_old[0] = m.corners(ca_old[2].opposite); + else + ca_old[0] = m.corners(ca_old[1].prev); + ca_old[1] = m.corners(ca_old[0].next); + ca_old[2] = m.corners(ca_old[0].prev); + cb_old[0] = m.corners(ca_old[0].opposite); + cb_old[1] = m.corners(cb_old[0].next); + cb_old[2] = m.corners(cb_old[0].prev); + + /////////////// + // avoid creating nonmanifold edges... again + ring0 = m.get1Ring(ca_old[2].node).nodes; + ring1 = m.get1Ring(ca_old[1].node).nodes; + + // check for intersections of the 1-rings of P0,P1 + cl=0; + for(set<int>::iterator it=ring1.begin(); it != ring1.end(); ++it) + if (*it != ca_old[0].node && ring0.find(*it) != ring0.end()) + cl++; + + if(cl>2) { // nonmanifold + // this can happen if collapsing the first tet leads to another similar collapse that requires the collapse of a tet. + // for now, just move on and pick this up later. + + // if the original component was very small, this first collapse could have led to a tiny piece of nonmanifold geometry. + // in this case, just delete everything that remains. + if(m.corners(ca_old[0].opposite).tri==cb_old[0].tri && m.corners(ca_old[1].opposite).tri==cb_old[0].tri && m.corners(ca_old[2].opposite).tri==cb_old[0].tri) { + taintedTris[ca_old[0].tri] = true; + taintedTris[cb_old[0].tri] = true; + m.removeTriFromLookup(ca_old[0].tri); + m.removeTriFromLookup(cb_old[0].tri); + deletedNodes.push_back(ca_old[0].node); + deletedNodes.push_back(ca_old[1].node); + deletedNodes.push_back(ca_old[2].node); + } + return; + } + } else if(topTet && botTet && ca_old[1].opposite>=0 && ca_old[2].opposite>=0 && cb_old[1].opposite>=0 && cb_old[2].opposite>=0) + { + if(!(m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node && + m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node && + (m.corners(ca_old[1].opposite).node == m.corners(cb_old[1].opposite).node || + (m.corners(ca_old[1].opposite).node == cb_old[0].node && + m.corners(cb_old[1].opposite).node == ca_old[0].node) ))) + { + // just collapse one for now. + + // collapse the whole tet! + // First collapse the top of the pyramid, + // then carry on collapsing the original verts. + Corner cc_old[3],cd_old[3]; + + // collapse top + { + cc_old[0] = m.corners(ca_old[1].opposite); + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + m.removeTriFromLookup(tmpTrinum); + m.removeTriFromLookup(tmpOthertri); + deletedNodes.push_back(P3); + + numCollapses++; + } + // then collapse bottom + { + //cc_old[0] = [ca_old[1].opposite; + cc_old[0] = cb_old[2]; + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + deletedNodes.push_back(P3); + + numCollapses++; + } + + // Though we've collapsed a lot of stuff, we still haven't collapsed the original edge. + // At this point we still haven't guaranteed that this original collapse weill be safe. + // quit for now, and we'll catch the remaining short edges the next time this function is called. + return; + } + } + else if (doTubeCutting) + { + // tube case + //cout<<"CollapseEdge:tube case" << endl; + + // find the edges that touch the common vert + int P2 = commonVert; + int P1P2=-1, P2P1, P2P0=-1, P0P2=-1; // corners across from the cutting seam + int start = ca_old[0].next; + int end = cb_old[0].prev; + int current = start; + do { + // rotate around vertex P1 counter-clockwise + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) throw Error("tube cutting failed, no opposite"); + current = m.corners(op).next; + + if(m.corners(m.corners(current).prev).node==commonVert) + P1P2 = m.corners(current).next; + } + while(current != end); + + start = ca_old[0].prev; + end = cb_old[0].next; + current = start; + do { + // rotate around vertex P0 clockwise + int op = m.corners(m.corners(current).prev).opposite; + if (op < 0) throw Error("tube cutting failed, no opposite"); + + current = m.corners(op).prev; + if(m.corners(m.corners(current).next).node==commonVert) + P2P0 = m.corners(current).prev; + } while(current != end); + + if (P1P2 < 0 || P2P0 < 0) + throw Error("tube cutting failed, ill geometry"); + + P2P1 = m.corners(P1P2).opposite; + P0P2 = m.corners(P2P0).opposite; + + // duplicate vertices on the top half of the cut, + // and use them to split the tube at this seam + int P0b = m.addNode(Node(m.nodes(P0).pos)); + int P1b = m.addNode(Node(m.nodes(P1).pos)); + int P2b = m.addNode(Node(m.nodes(P2).pos)); + for (int i=0; i<m.numNodeChannels(); i++) { + m.nodeChannel(i)->addInterpol(P0, P0, 0.5); + m.nodeChannel(i)->addInterpol(P1, P1, 0.5); + m.nodeChannel(i)->addInterpol(P2, P2, 0.5); + } + + // offset the verts in the normal directions to avoid self intersections + Vec3 offsetVec = cross(m.nodes(P1).pos-m.nodes(P0).pos, m.nodes(P2).pos-m.nodes(P0).pos); + normalize(offsetVec); + offsetVec *= 0.01; // HACK: + m.nodes(P0).pos -= offsetVec; + m.nodes(P1).pos -= offsetVec; + m.nodes(P2).pos -= offsetVec; + m.nodes(P0b).pos += offsetVec; + m.nodes(P1b).pos += offsetVec; + m.nodes(P2b).pos += offsetVec; + + // create a list of all triangles which touch P0, P1, and P2 from the top, + map<int,bool> topTris; + start = cb_old[0].next; + end = m.corners(P0P2).prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + start = m.corners(P0P2).next; + end = m.corners(P2P1).prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + start = m.corners(P2P1).next; + end = cb_old[0].prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + + // create two new triangles, + int Ta = m.addTri(Triangle(P0,P1,P2)); + int Tb = m.addTri(Triangle(P1b,P0b,P2b)); + for (int i=0; i<m.numTriChannels(); i++) { + m.triChannel(i)->addNew(); + m.triChannel(i)->addNew(); + } + + // sew the tris to close the cut on each side + for(int c=0; c<3; c++) m.addCorner(Corner(Ta, m.tris(Ta).c[c])); + for(int c=0; c<3; c++) m.addCorner(Corner(Tb, m.tris(Tb).c[c])); + for(int c=0; c<3; c++) { + m.corners(Ta,c).next = 3*Ta+((c+1)%3); + m.corners(Ta,c).prev = 3*Ta+((c+2)%3); + m.corners(Tb,c).next = 3*Tb+((c+1)%3); + m.corners(Tb,c).prev = 3*Tb+((c+2)%3); + } + m.corners(Ta,0).opposite = P1P2; + m.corners(Ta,1).opposite = P2P0; + m.corners(Ta,2).opposite = ca_old[1].prev; + m.corners(Tb,0).opposite = P0P2; + m.corners(Tb,1).opposite = P2P1; + m.corners(Tb,2).opposite = cb_old[1].prev; + for (int c=0; c<3; c++) { + m.corners(m.corners(Ta,c).opposite).opposite = 3*Ta+c; + m.corners(m.corners(Tb,c).opposite).opposite = 3*Tb+c; + } + // replace P0,P1,P2 on the top with P0b,P1b,P2b. + for(map<int,bool>::iterator tti=topTris.begin(); tti!=topTris.end(); tti++) { + //cout << "H " << tti->first << " : " << m.tris(tti->first).c[0] << " " << m.tris(tti->first).c[1] << " " << m.tris(tti->first).c[2] << " " << endl; + for(int i=0; i<3; i++) { + int cn = m.tris(tti->first).c[i]; + set<int>& ring = m.get1Ring(cn).nodes; + + if (ring.find(P0) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P0); + ring.insert(P0b); + m.get1Ring(P0).nodes.erase(cn); + m.get1Ring(P0b).nodes.insert(cn); + } + if (ring.find(P1) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P1); + ring.insert(P1b); + m.get1Ring(P1).nodes.erase(cn); + m.get1Ring(P1b).nodes.insert(cn); + } + if (ring.find(P2) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P2); + ring.insert(P2b); + m.get1Ring(P2).nodes.erase(cn); + m.get1Ring(P2b).nodes.insert(cn); + } + if(cn==P0) { + m.tris(tti->first).c[i]=P0b; + m.corners(tti->first,i).node = P0b; + m.get1Ring(P0).tris.erase(tti->first); + m.get1Ring(P0b).tris.insert(tti->first); + } + else if(cn==P1) { + m.tris(tti->first).c[i]=P1b; + m.corners(tti->first,i).node = P1b; + m.get1Ring(P1).tris.erase(tti->first); + m.get1Ring(P1b).tris.insert(tti->first); + } + else if(cn==P2) { + m.tris(tti->first).c[i]=P2b; + m.corners(tti->first,i).node = P2b; + m.get1Ring(P2).tris.erase(tti->first); + m.get1Ring(P2b).tris.insert(tti->first); + } + } + } + + //m.sanityCheck(true, &deletedNodes, &taintedTris); + + return; + } + return; + } + if(ca_old[1].opposite>=0 && ca_old[2].opposite>=0 && cb_old[1].opposite>=0 && cb_old[2].opposite>=0 && ca_old[0].opposite>=0 && cb_old[0].opposite>=0 && + ((m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node && // two-pyramid tubey case (6 tris, 5 verts) + m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node && + (m.corners(ca_old[1].opposite).node == m.corners(cb_old[1].opposite).node || + (m.corners(ca_old[1].opposite).node==cb_old[0].node && // single tetrahedron case + m.corners(cb_old[1].opposite).node==ca_old[0].node) )) + || + (m.corners(ca_old[0].opposite).tri==m.corners(cb_old[0].opposite).tri && m.corners(ca_old[1].opposite).tri==m.corners(cb_old[0].opposite).tri && m.corners(ca_old[2].opposite).tri==m.corners(cb_old[0].opposite).tri // nonmanifold: 2 tris, 3 verts + && m.corners(cb_old[0].opposite).tri==m.corners(ca_old[0].opposite).tri && m.corners(cb_old[1].opposite).tri==m.corners(ca_old[0].opposite).tri && m.corners(cb_old[2].opposite).tri==m.corners(ca_old[0].opposite).tri) + )) + { + // both top and bottom are closed pyramid caps, or it is a single tet + // delete the whole component! + // flood fill to mark all triangles in the component + map<int,bool> markedTris; + queue<int> triQ; + triQ.push(trinum); + markedTris[trinum] = true; + int iters = 0; + while(!triQ.empty()) { + int trival = triQ.front(); + triQ.pop(); + for(int i=0; i<3; i++) { + int newtri = m.corners(m.corners(trival,i).opposite).tri; + if(markedTris.find(newtri)==markedTris.end()) { + triQ.push(newtri); + markedTris[newtri] = true; + } + } + iters++; + } + map<int,bool> markedverts; + for(map<int,bool>::iterator mit=markedTris.begin(); mit!=markedTris.end(); mit++) { + taintedTris[mit->first] = true; + markedverts[m.tris(mit->first).c[0]] = true; + markedverts[m.tris(mit->first).c[1]] = true; + markedverts[m.tris(mit->first).c[2]] = true; + } + for(map<int,bool>::iterator mit=markedverts.begin(); mit!=markedverts.end(); mit++) + deletedNodes.push_back(mit->first); + return; + } + + ////////////////////////// + // begin original edge collapse + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P0,P1, ca_old[0], cb_old[0]); + + m.mergeNode(P0, P1); + + // Move position of P0 + m.nodes(P0).pos = endpoint + 0.5*edgevect; + + // Preserve connectivity in both triangles + if (ca_old[1].opposite>=0) + m.corners(ca_old[1].opposite).opposite = ca_old[2].opposite; + if (ca_old[2].opposite>=0) + m.corners(ca_old[2].opposite).opposite = ca_old[1].opposite; + if (haveB && cb_old[1].opposite>=0) + m.corners(cb_old[1].opposite).opposite = cb_old[2].opposite; + if (haveB && cb_old[2].opposite>=0) + m.corners(cb_old[2].opposite).opposite = cb_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + taintedTris[ca_old[0].tri] = true; + m.removeTriFromLookup(ca_old[0].tri); + if (haveB) { + taintedTris[cb_old[0].tri] = true; + m.removeTriFromLookup(cb_old[0].tri); + } + deletedNodes.push_back(P1); + numCollapses++; +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/edgecollapse.h b/source/blender/python/manta_full/source/edgecollapse.h new file mode 100644 index 00000000000..1b704d56ac7 --- /dev/null +++ b/source/blender/python/manta_full/source/edgecollapse.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Mesh edge collapse and subdivision + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#ifndef _EDGECOLLAPSE_H +#define _EDGECOLLAPSE_H + +#include "mesh.h" + +namespace Manta { + +void CollapseEdge(Mesh& mesh, const int trinum, const int which, const Vec3 &edgevect, const Vec3 &endpoint, + std::vector<int> &deletedNodes, std::map<int,bool> &taintedTris, int &numCollapses, bool doTubeCutting); + +Vec3 ModifiedButterflySubdivision(Mesh& mesh, const Corner& ca, const Corner& cb, const Vec3& fallback); + +} + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/fastmarch.cpp b/source/blender/python/manta_full/source/fastmarch.cpp new file mode 100644 index 00000000000..a801732f33b --- /dev/null +++ b/source/blender/python/manta_full/source/fastmarch.cpp @@ -0,0 +1,424 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Fast marching and extrapolation + * + ******************************************************************************/ + +#include "fastmarch.h" +#include "levelset.h" +#include "kernel.h" +#include <algorithm> + +using namespace std; + +namespace Manta { + +template<class COMP, int TDIR> +FastMarch<COMP,TDIR>::FastMarch(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& levelset, Real maxTime, + MACGrid* velTransport, Grid<Real>* velMag ) + : mLevelset(levelset), mFlags(flags), mFmFlags(fmFlags) +{ + if (velTransport) + mVelTransport.initMarching(velTransport, &flags); + if (velMag) + mMagTransport.initMarching(velMag, &flags); + + mMaxTime = maxTime * TDIR; +} + +// helper for individual components to calculateDistance +template<class COMP, int TDIR> template<int C> +Real FastMarch<COMP,TDIR>::calcWeights(int& okcnt, int& invcnt, Real* v, const Vec3i& idx) { + Real val = 0.; + Vec3i idxPlus(idx), idxMinus(idx); + idxPlus[C]++; + idxMinus[C]--; + + mWeights[C*2] = mWeights[C*2+1] = 0.; + if (mFmFlags(idxPlus)==FlagInited) { + // somewhat arbitrary - choose +1 value over -1 ... + val = mLevelset(idxPlus); + v[okcnt] = val; okcnt++; + mWeights[C*2] = 1.; + } else if (mFmFlags(idxMinus)==FlagInited) { + val = mLevelset(idxMinus); + v[okcnt] = val; okcnt++; + mWeights[C*2+1] = 1.; + } + else { + invcnt++; + } + return val; +} + +template<class COMP, int TDIR> +inline Real FastMarch<COMP,TDIR>::calculateDistance(const Vec3i& idx) { + //int invflag = 0; + int invcnt = 0; + Real v[3]; + int okcnt = 0; + + Real aVal = calcWeights<0>(okcnt, invcnt, v, idx); + Real bVal = calcWeights<1>(okcnt, invcnt, v, idx); + Real cVal = 0.; + if (mLevelset.is3D()) cVal = calcWeights<2>(okcnt, invcnt, v, idx); + else { invcnt++; mWeights[4] = mWeights[5] = 0.; } + + Real ret = InvalidTime(); + switch(invcnt) { + case 0: { + // take all values + const Real ca=v[0], cb=v[1], cc=v[2]; + const Real csqrt = max(0. , + -2.*(ca*ca+cb*cb- cb*cc + cc*cc - ca*(cb+cc)) + 3 ); + // clamp to make sure the sqrt is valid + ret = 0.333333*( ca+cb+cc+ TDIR*sqrt(csqrt) ); + + // weights needed for transport (transpTouch) + mWeights[0] *= fabs(ret-ca); + mWeights[1] *= fabs(ret-ca); + mWeights[2] *= fabs(ret-cb); + mWeights[3] *= fabs(ret-cb); + mWeights[4] *= fabs(ret-cc); + mWeights[5] *= fabs(ret-cc); + + Real norm = 0.0; // try to force normalization + for(int i=0;i<6;i++) { + norm += mWeights[i]; + } + norm = 1.0/norm; + for(int i=0;i<6;i++) { mWeights[i] *= norm; } + + } break; + case 1: { + // take just the 2 ok values + // t=0.5*( a+b+ (2*g*g-(b-a)*(b-a))^0.5) + const Real csqrt = max(0. , 2.-(v[1]-v[0])*(v[1]-v[0]) ); + // clamp to make sure the sqrt is valid + ret = 0.5*( v[0]+v[1]+ TDIR*sqrt(csqrt) ); + + // weights needed for transport (transpTouch) + mWeights[0] *= fabs(ret-aVal); + mWeights[1] *= fabs(ret-aVal); + mWeights[2] *= fabs(ret-bVal); + mWeights[3] *= fabs(ret-bVal); + mWeights[4] *= fabs(ret-cVal); + mWeights[5] *= fabs(ret-cVal); + + Real norm = 0.0; // try to force normalization + for(int i=0;i<6;i++) { + norm += mWeights[i]; + } + norm = 1.0/norm; + for(int i=0;i<6;i++) { mWeights[i] *= norm; } + // */ + + } break; + case 2: { + // just use the one remaining value + ret = v[0]+ (Real)(TDIR) ; // direction = +- 1 + } break; + default: + throw Error("FastMarch :: Invalid invcnt"); + break; + } + return ret; +} + +template<class COMP, int TDIR> +void FastMarch<COMP,TDIR>::addToList(const Vec3i& p, const Vec3i& src) { + if (!mLevelset.isInBounds(p,1)) return; + const int idx = mLevelset.index(p); + + // already known value, value alreay set to valid value? skip cell... + if(mFmFlags[idx] == FlagInited) return; + + // discard by source time now , TODO do instead before calling all addtolists? + Real srct = mLevelset(src); + if(COMP::compare(srct, mMaxTime)) return; + + Real ttime = calculateDistance(p); + + // remove old entry if larger + bool found=false; + + Real oldt = mLevelset[idx]; + if (mFmFlags[idx] == FlagIsOnHeap) { + found = true; + // is old time better? + if(COMP::compare(ttime,oldt)) return; + } + + // update field + mFmFlags[idx] = FlagIsOnHeap; + mLevelset[idx] = ttime; + + if (mVelTransport.isInitialized()) + mVelTransport.transpTouch(p.x, p.y, p.z, mWeights, ttime); + if (mMagTransport.isInitialized()) + mMagTransport.transpTouch(p.x, p.y, p.z, mWeights, ttime); + + // the following adds entries to the heap of active cells + // current: (!found) , previous: always add, might lead to duplicate + // entries, but the earlier will be handled earlier, the second one will skip to the FlagInited check above + if(!found) + { + // add list entry with source value + COMP entry; + entry.p = p; + entry.time = mLevelset[idx]; + + mHeap.push( entry ); + // debug info std::cout<<"push "<< entry.p <<","<< entry.time <<"\n"; + } + +} + +//! Enforce delta_phi = 0 on boundaries +KERNEL(single) +void SetLevelsetBoundaries (LevelsetGrid& phi) { + if (i==0) phi(i,j,k) = phi(1,j,k); + if (i==maxX-1) phi(i,j,k) = phi(i-1,j,k); + + if (j==0) phi(i,j,k) = phi(i,1,k); + if (j==maxY-1) phi(i,j,k) = phi(i,j-1,k); + + if(phi.is3D()) { + if (k==0) phi(i,j,k) = phi(i,j,1); + if (k==maxZ-1) phi(i,j,k) = phi(i,j,k-1); + } +} + +/*****************************************************************************/ +//! Walk... +template<class COMP, int TDIR> +void FastMarch<COMP,TDIR>::performMarching() { + mReheapVal = 0.0; + while(mHeap.size() > 0) { + + const COMP& ce = mHeap.top(); + Vec3i p = ce.p; + mFmFlags(p) = FlagInited; + mHeap.pop(); + // debug info std::cout<<"pop "<< ce.p <<","<< ce.time <<"\n"; + + addToList(Vec3i(p.x-1,p.y,p.z), p); + addToList(Vec3i(p.x+1,p.y,p.z), p); + addToList(Vec3i(p.x,p.y-1,p.z), p); + addToList(Vec3i(p.x,p.y+1,p.z), p); + if(mLevelset.is3D()) { + addToList(Vec3i(p.x,p.y,p.z-1), p); + addToList(Vec3i(p.x,p.y,p.z+1), p); + } + } + + // set boundary for plain array + SetLevelsetBoundaries setls(mLevelset); +} + +// explicit instantiation +template class FastMarch<FmHeapEntryIn, -1>; +template class FastMarch<FmHeapEntryOut, +1>; + + +/*****************************************************************************/ +// simpler extrapolation functions (primarily for FLIP) + +KERNEL(bnd=1) +void knExtrapolateMACSimple (MACGrid& vel, int distance , Grid<int>& tmp , const int d , const int c ) +{ + static const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + const int dim = (vel.is3D() ? 3:2); + + if (tmp(i,j,k) != 0) return; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + Real avgVel = 0.; + for (int n=0; n<2*dim; ++n) { + if (tmp(p+nb[n]) == d) { + //vel(p)[c] = (c+1.)*0.1; + avgVel += vel(p+nb[n])[c]; + nbs++; + } + } + + if(nbs>0) { + tmp(p) = d+1; + vel(p)[c] = avgVel / nbs; + } +} +KERNEL(bnd=0) +void knExtrapolateIntoBnd (FlagGrid& flags, MACGrid& vel) +{ + int c=0; + Vec3 v(0,0,0); + if( i==0 ) { + v = vel(i+1,j,k); + if(v[0] < 0.) v[0] = 0.; + c++; + } + else if( i==(flags.getSizeX()-1) ) { + v = vel(i-1,j,k); + if(v[0] > 0.) v[0] = 0.; + c++; + } + if( j==0 ) { + v = vel(i,j+1,k); + if(v[1] < 0.) v[1] = 0.; + c++; + } + else if( j==(flags.getSizeY()-1) ) { + v = vel(i,j-1,k); + if(v[1] > 0.) v[1] = 0.; + c++; + } + if(flags.is3D()) { + if( k==0 ) { + v = vel(i,j,k+1); + if(v[2] < 0.) v[2] = 0.; + c++; + } + else if( k==(flags.getSizeY()-1) ) { + v = vel(i,j,k-1); + if(v[2] > 0.) v[2] = 0.; + c++; + } } + if(c>0) { + vel(i,j,k) = v/(Real)c; + } +} + +inline Vec3 getNormal(const Grid<Real>& data, int i, int j, int k) { + if (i > data.getSizeX()-2) i= data.getSizeX()-2; + if (i < 1) i = 1; + if (j > data.getSizeY()-2) j= data.getSizeY()-2; + if (j < 1) j = 1; + + int kd = 1; + if(data.is3D()) { + if (k > data.getSizeZ()-2) k= data.getSizeZ()-2; + if (k < 1) k = 1; + } else { kd=0; } + + return Vec3( data(i+1,j ,k ) - data(i-1,j ,k ) , + data(i ,j+1,k ) - data(i ,j-1,k ) , + data(i ,j ,k+kd) - data(i ,j ,k-kd) ); +} +KERNEL(bnd=1) +void knUnprojectNormalComp (FlagGrid& flags, MACGrid& vel, LevelsetGrid& phi, Real maxDist) +{ + // apply inside, within range near obstacle surface + if(phi(i,j,k)>0. || phi(i,j,k)<-maxDist) return; + + Vec3 n = getNormal(phi, i,j,k); + Vec3 v = vel(i,j,k); + if(dot(n,v) < 0.) { + normalize(n); + Real l = dot(n,v); + vel(i,j,k) -= n*l; + } +} +// a simple extrapolation step , used for cases where there's no levelset +// (note, less accurate than fast marching extrapolation!) +PYTHON void extrapolateMACSimple (FlagGrid& flags, MACGrid& vel, int distance = 4, LevelsetGrid* phiObs=NULL ) +{ + Grid<int> tmp( flags.getParent() ); + int dim = (flags.is3D() ? 3:2); + + for(int c=0; c<dim; ++c) { + Vec3i dir = 0; + dir[c] = 1; + tmp.clear(); + + // remove all fluid cells + FOR_IJK_BND(flags,1) { + Vec3i p(i,j,k); + if (flags.isFluid(p) || flags.isFluid(p-dir) ) { + tmp(p) = 1; + } + } + + // debug init! , enable for testing only - set varying velocities inside + //FOR_IJK_BND(flags,1) { if (tmp(i,j,k) == 0) continue; vel(i,j,k)[c] = (i+j+k+c+1.)*0.1; } + + // extrapolate for distance + for(int d=1; d<1+distance; ++d) { + knExtrapolateMACSimple(vel, distance, tmp, d, c); + } // d + } + + if(phiObs) { + knUnprojectNormalComp( flags, vel, *phiObs, distance ); + } + + // copy tangential values into sides + knExtrapolateIntoBnd(flags, vel); +} + +KERNEL(bnd=1) +void knExtrapolateMACFromWeight ( MACGrid& vel, Grid<Vec3>& weight, int distance , const int d, const int c ) +{ + static const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + const int dim = (vel.is3D() ? 3:2); + + if (weight(i,j,k)[c] != 0) return; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + Real avgVel = 0.; + for (int n=0; n<2*dim; ++n) { + if (weight(p+nb[n])[c] == d) { + avgVel += vel(p+nb[n])[c]; + nbs++; + } + } + + if(nbs>0) { + weight(p)[c] = d+1; + vel(p)[c] = avgVel / nbs; + } +} +// same as extrapolateMACSimple, but uses weight vec3 grid instead of flags to check +// for valid values (to be used in combination with mapPartsToMAC) +// note - the weight grid values are destroyed! the function is necessary due to discrepancies +// between velocity mapping on surface-levelset / fluid-flag creation. With this +// extrapolation we make sure the fluid region is covered by initial velocities +PYTHON void extrapolateMACFromWeight ( MACGrid& vel, Grid<Vec3>& weight, int distance = 2) +{ + const int dim = (vel.is3D() ? 3:2); + + for(int c=0; c<dim; ++c) { + Vec3i dir = 0; + dir[c] = 1; + + // reset weight values to 0 (uninitialized), and 1 (initialized inner values) + FOR_IJK_BND(vel,1) { + Vec3i p(i,j,k); + if(weight(p)[c]>0.) weight(p)[c] = 1.0; + } + + // extrapolate for distance + for(int d=1; d<1+distance; ++d) { + knExtrapolateMACFromWeight(vel, weight, distance, d, c); + } // d + + } +} + +} // namespace diff --git a/source/blender/python/manta_full/source/fastmarch.h b/source/blender/python/manta_full/source/fastmarch.h new file mode 100644 index 00000000000..a6f7759097e --- /dev/null +++ b/source/blender/python/manta_full/source/fastmarch.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Fast marching + * + ******************************************************************************/ + +#ifndef _FASTMARCH_H +#define _FASTMARCH_H + +#include <queue> +#include "levelset.h" + +namespace Manta { + +//! Fast marching. Transport certain values +// This class exists in two versions: for scalar, and for vector values - the only difference are +// flag checks i transpTouch (for simplicity in separate classes) + +template<class GRID, class T> +inline T fmInterpolateNeighbors(GRID* mpVal, int x,int y,int z, Real *weights) { + T val(0.); + if(weights[0]>0.0) val += mpVal->get(x+1, y+0, z+0) * weights[0]; + if(weights[1]>0.0) val += mpVal->get(x-1, y+0, z+0) * weights[1]; + if(weights[2]>0.0) val += mpVal->get(x+0, y+1, z+0) * weights[2]; + if(weights[3]>0.0) val += mpVal->get(x+0, y-1, z+0) * weights[3]; + if(mpVal->is3D()) { + if(weights[4]>0.0) val += mpVal->get(x+0, y+0, z+1) * weights[4]; + if(weights[5]>0.0) val += mpVal->get(x+0, y+0, z-1) * weights[5]; + } + return val; +} + +template<class GRID, class T> +class FmValueTransportScalar { +public: + FmValueTransportScalar() : mpVal(0),mpFlags(0) { }; + ~FmValueTransportScalar() { }; + void initMarching(GRID* val, FlagGrid* flags) { + mpVal = val; + mpFlags = flags; + } + inline bool isInitialized() { return mpVal != 0; } + + //! cell is touched by marching from source cell + inline void transpTouch(int x,int y,int z, Real *weights, Real time) { + if(!mpVal || !mpFlags->isEmpty(x,y,z)) return; + T val = fmInterpolateNeighbors<GRID,T>(mpVal,x,y,z,weights); + (*mpVal)(x,y,z) = val; + }; +protected: + GRID* mpVal; + FlagGrid* mpFlags; +}; + +template<class GRID, class T> +class FmValueTransportVec3 { +public: + FmValueTransportVec3() : mpVal(0), mpFlags(0) { }; + ~FmValueTransportVec3() { }; + inline bool isInitialized() { return mpVal != 0; } + void initMarching(GRID* val, FlagGrid* flags) { + mpVal = val; + mpFlags = flags; + } + + //! cell is touched by marching from source cell + inline void transpTouch(int x,int y,int z, Real *weights, Real time) { + if(!mpVal || !mpFlags->isEmpty(x,y,z)) return; + //if(!mpVal) return; + + T val = fmInterpolateNeighbors<GRID,T>(mpVal,x,y,z,weights); /*T(0.); + if(weights[0]>0.0) val += mpVal->get(x+1, y+0, z+0) * weights[0]; + if(weights[1]>0.0) val += mpVal->get(x-1, y+0, z+0) * weights[1]; + if(weights[2]>0.0) val += mpVal->get(x+0, y+1, z+0) * weights[2]; + if(weights[3]>0.0) val += mpVal->get(x+0, y-1, z+0) * weights[3]; + if(mpVal->is3D()) { + if(weights[4]>0.0) val += mpVal->get(x+0, y+0, z+1) * weights[4]; + if(weights[5]>0.0) val += mpVal->get(x+0, y+0, z-1) * weights[5]; + }*/ + + // set velocity components if adjacent is empty + if (mpFlags->isEmpty(x-1,y,z)) (*mpVal)(x,y,z).x = val.x; + if (mpFlags->isEmpty(x,y-1,z)) (*mpVal)(x,y,z).y = val.y; + if(mpVal->is3D()) { if (mpFlags->isEmpty(x,y,z-1)) (*mpVal)(x,y,z).z = val.z; } + //(*mpVal)(x,y,z).x = val.x; + //(*mpVal)(x,y,z).y = val.y; + //if(mpVal->is3D()) { (*mpVal)(x,y,z).z = val.z; } + }; + +protected: + GRID* mpVal; + FlagGrid* mpFlags; +}; + +class FmHeapEntryOut { +public: + Vec3i p; + // quick time access for sorting + Real time; + static inline bool compare(const Real x, const Real y) { + return x > y; + } + + inline bool operator< (const FmHeapEntryOut& o) const { + const Real d = fabs((time) - ((o.time))); + if (d > 0.) return (time) > ((o.time)); + if (p.z != o.p.z) return p.z > o.p.z; + if (p.y != o.p.y) return p.y > o.p.y; + return p.x > o.p.x; + }; + +}; + +class FmHeapEntryIn { +public: + Vec3i p; + // quick time access for sorting + Real time; + static inline bool compare(const Real x, const Real y) { + return x < y; + } + + inline bool operator< (const FmHeapEntryIn& o) const { + const Real d = fabs((time) - ((o.time))); + if (d > 0.) return (time) < ((o.time)); + if (p.z != o.p.z) return p.z < o.p.z; + if (p.y != o.p.y) return p.y < o.p.y; + return p.x < o.p.x; + }; +}; + + +//! fast marching algorithm wrapper class +template<class T, int TDIR> +class FastMarch { + +public: + // MSVC doesn't allow static const variables in template classes + static inline Real InvalidTime() { return -1000; } + static inline Real InvtOffset() { return 500; } + + enum SpecialValues { FlagInited = 1, FlagIsOnHeap = 2}; + + FastMarch(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& levelset, Real maxTime, + MACGrid* velTransport = NULL, Grid<Real>* velMag = NULL); + ~FastMarch() {} + + //! advect level set function with given velocity */ + void performMarching(); + + //! test value for invalidity + inline bool isInvalid(Real v) const { return (v <= InvalidTime()); } + + void addToList(const Vec3i& p, const Vec3i& src); + + //! convert phi to time value + inline Real phi2time(Real phival) { return (phival-InvalidTime()+ InvtOffset()) * -1.0; } + + //! ... and back + inline Real time2phi(Real tval) { return (InvalidTime() - InvtOffset() - tval); } + + inline Real _phi(int i, int j, int k) { return mLevelset(i,j,k); } +protected: + LevelsetGrid& mLevelset; + FlagGrid& mFlags; + Grid<int>& mFmFlags; + + //! velocity extrpolation + FmValueTransportVec3<MACGrid , Vec3> mVelTransport; + FmValueTransportScalar<Grid<Real>, Real> mMagTransport; + + //! maximal time to march for + Real mMaxTime; + + //! fast marching list + std::priority_queue<T, std::vector<T>, std::less<T> > mHeap; + Real mReheapVal; + + //! weights for touching points + Real mWeights[6]; + + template<int C> inline Real calcWeights(int& okCnt, int& invcnt, Real* v, const Vec3i& idx); + + inline Real calculateDistance(const Vec3i& pos); +}; + +} // namespace +#endif + diff --git a/source/blender/python/manta_full/source/fileio.cpp b/source/blender/python/manta_full/source/fileio.cpp new file mode 100644 index 00000000000..dd1557f397c --- /dev/null +++ b/source/blender/python/manta_full/source/fileio.cpp @@ -0,0 +1,700 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Loading and writing grids and meshes to disk + * + ******************************************************************************/ + +#include <iostream> +#include <fstream> +#include <cstdlib> +#if NO_ZLIB!=1 +extern "C" { +#include <zlib.h> +} +#endif + +#include "fileio.h" +#include "grid.h" +#include "mesh.h" +#include "vortexsheet.h" +#include "particle.h" +#include <cstring> + +using namespace std; + +namespace Manta { + +//***************************************************************************** +// mesh data +//***************************************************************************** + +void writeBobjFile(const string& name, Mesh* mesh) { + cout << "writing mesh file " << name << endl; +# if NO_ZLIB!=1 + const Real dx = mesh->getParent()->getDx(); + const Vec3i gs = mesh->getParent()->getGridSize(); + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) + errMsg("writeBobj: unable to open file"); + + // write vertices + int numVerts = mesh->numNodes(); + gzwrite(gzf, &numVerts, sizeof(int)); + for (int i=0; i<numVerts; i++) { + Vector3D<float> pos = toVec3f(mesh->nodes(i).pos); + // normalize + pos -= toVec3f(gs)*0.5; + pos *= dx; + gzwrite(gzf, &pos.value[0], sizeof(float)*3); + } + + // normals + mesh->computeVertexNormals(); + gzwrite(gzf, &numVerts, sizeof(int)); + for (int i=0; i<numVerts; i++) { + Vector3D<float> pos = toVec3f(mesh->nodes(i).normal); + gzwrite(gzf, &pos.value[0], sizeof(float)*3); + } + + // write tris + int numTris = mesh->numTris(); + gzwrite(gzf, &numTris, sizeof(int)); + for(int t=0; t<numTris; t++) { + for(int j=0; j<3; j++) { + int trip = mesh->tris(t).c[j]; + gzwrite(gzf, &trip, sizeof(int)); + } + } + + // per vertex smoke densities + if (mesh->getType() == Mesh::TypeVortexSheet) { + VortexSheetMesh* vmesh = (VortexSheetMesh*) mesh; + int densId[4] = {0, 'v','d','e'}; + gzwrite(gzf, &densId[0], sizeof(int) * 4); + + // compute densities + vector<float> triDensity(numTris); + for (int tri=0; tri < numTris; tri++) { + Real area = vmesh->getFaceArea(tri); + if (area>0) + triDensity[tri] = vmesh->sheet(tri).smokeAmount; + } + + // project triangle data to vertex + vector<int> triPerVertex(numVerts); + vector<float> density(numVerts); + for (int tri=0; tri < numTris; tri++) { + for (int c=0; c<3; c++) { + int vertex = mesh->tris(tri).c[c]; + density[vertex] += triDensity[tri]; + triPerVertex[vertex]++; + } + } + + // averaged smoke densities + for(int point=0; point<numVerts; point++) { + float dens = 0; + if (triPerVertex[point]>0) + dens = density[point] / triPerVertex[point]; + gzwrite(gzf, &dens, sizeof(float)); + } + } + + // vertex flags + if (mesh->getType() == Mesh::TypeVortexSheet) { + int Id[4] = {0, 'v','x','f'}; + gzwrite(gzf, &Id[0], sizeof(int) * 4); + + // averaged smoke densities + for(int point=0; point<numVerts; point++) { + float alpha = (mesh->nodes(point).flags & Mesh::NfMarked) ? 1: 0; + gzwrite(gzf, &alpha, sizeof(float)); + } + } + + gzclose( gzf ); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +void readObjFile(const std::string& name, Mesh* mesh, bool append) { + ifstream ifs (name.c_str()); + + if (!ifs.good()) + errMsg("can't open file '" + name + "'"); + + if (!append) + mesh->clear(); + int nodebase = mesh->numNodes(); + while(ifs.good() && !ifs.eof()) { + string id; + ifs >> id; + + if (id[0] == '#') { + // comment + getline(ifs, id); + continue; + } + if (id == "vt") { + // tex coord, ignore + } else if (id == "vn") { + // normals, ignore + } else if (id == "v") { + // vertex + Node n; + ifs >> n.pos.x >> n.pos.y >> n.pos.z; + mesh->addNode(n); + } else if (id == "g") { + // group + string group; + ifs >> group; + } else if (id == "f") { + // face + string face; + Triangle t; + for (int i=0; i<3; i++) { + ifs >> face; + if (face.find('/') != string::npos) + face = face.substr(0, face.find('/')); // ignore other indices + int idx = atoi(face.c_str()) - 1; + if (idx < 0) + errMsg("invalid face encountered"); + idx += nodebase; + t.c[i] = idx; + } + mesh->addTri(t); + } else { + // whatever, ignore + } + // kill rest of line + getline(ifs, id); + } + ifs.close(); +} + +void writeObjFile(const string& name, Mesh* mesh) { + errMsg("obj exporter not yet implemented"); +} + +//***************************************************************************** +// grid data +//***************************************************************************** + +template<class T> +void writeGridTxt(const string& name, Grid<T>* grid) { + cout << "writing grid " << grid->getName() << " to text file " << name << endl; + + ofstream ofs(name.c_str()); + if (!ofs.good()) + errMsg("can't open file!"); + FOR_IJK(*grid) { + ofs << Vec3i(i,j,k) <<" = "<< (*grid)(i,j,k) <<"\n"; + } + ofs.close(); +} + +template<class T> +void writeGridRaw(const string& name, Grid<T>* grid) { + cout << "writing grid " << grid->getName() << " to raw file " << name << endl; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + gzwrite(gzf, &((*grid)[0]), sizeof(T)*grid->getSizeX()*grid->getSizeY()*grid->getSizeZ()); + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +template<class T> +void readGridRaw(const string& name, Grid<T>* grid) { + cout << "reading grid " << grid->getName() << " from raw file " << name << endl; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + int bytes = sizeof(T)*grid->getSizeX()*grid->getSizeY()*grid->getSizeZ(); + int readBytes = gzread(gzf, &((*grid)[0]), bytes); + assertMsg(bytes==readBytes, "can't read raw file, stream length does not match"<<bytes<<" vs "<<readBytes); + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +//! legacy headers for reading old files +typedef struct { + int dimX, dimY, dimZ; + int frames, elements, elementType, bytesPerElement, bytesPerFrame; +} UniLegacyHeader; + +typedef struct { + int dimX, dimY, dimZ; + int gridType, elementType, bytesPerElement; +} UniLegacyHeader2; + +//! uni file header +typedef struct { + int dimX, dimY, dimZ; // grid size + int gridType, elementType, bytesPerElement; // data type info + char info[256]; // mantaflow build information + unsigned long long timestamp; // creation time +} UniHeader; + +//! for test run debugging +PYTHON() void printUniFileInfoString(const string& name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (gzf) { + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + if (!strcmp(ID, "MNT2")) { + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + gzclose(gzf); + debMsg("File '"<<name<<"' info: "<< head.info ,1); + return; // all good! + } + gzclose(gzf); + } +# endif + debMsg("File '"<<name<<"', no valid info string found",1); +} + +//! for auto-init & check of results of test runs +PYTHON() Vec3 getUniFileSize(const string& name) { + Vec3 s(0.); +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (gzf) { + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + if (!strcmp(ID, "MNT2")) { + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + s = Vec3(head.dimX,head.dimY,head.dimZ); + } + gzclose(gzf); + } +# endif + return s; +} + +#if NO_ZLIB!=1 +template <class T> +void convertDoubleAndWrite(Grid<T>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + errMsg("unknown type, not yet supported"); +} + +template <> +void convertDoubleAndWrite(Grid<int>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + gzwrite(gzf, &head, sizeof(UniHeader)); + gzwrite(gzf, ptr, sizeof(int)*head.dimX*head.dimY*head.dimZ); +} + +template <> +void convertDoubleAndWrite(Grid<double>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + head.bytesPerElement = sizeof(float); + gzwrite(gzf, &head, sizeof(UniHeader)); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i,++ptrf) { + *ptrf = (float)grid[i]; + } + gzwrite(gzf, ptr, sizeof(float)* head.dimX*head.dimY*head.dimZ); +} + +template <> +void convertDoubleAndWrite(Grid<Vector3D<double> >& grid, void* ptr, gzFile& gzf, UniHeader& head) { + head.bytesPerElement = sizeof(Vector3D<float>); + gzwrite(gzf, &head, sizeof(UniHeader)); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i) { + for(int c=0; c<3; ++c) { *ptrf = (float)grid[i][c]; ptrf++; } + } + gzwrite(gzf, ptr, sizeof(float)*3 *head.dimX*head.dimY*head.dimZ); +} +#endif // NO_ZLIB!=1 + +template <class T> +void writeGridUni(const string& name, Grid<T>* grid) { + cout << "writing grid " << grid->getName() << " to uni file " << name << endl; + +# if NO_ZLIB!=1 + char ID[5] = "MNT2"; + UniHeader head; + head.dimX = grid->getSizeX(); + head.dimY = grid->getSizeY(); + head.dimZ = grid->getSizeZ(); + head.gridType = grid->getType(); + head.bytesPerElement = sizeof(T); + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + if (grid->getType() & GridBase::TypeInt) + head.elementType = 0; + else if (grid->getType() & GridBase::TypeReal) + head.elementType = 1; + else if (grid->getType() & GridBase::TypeVec3) + head.elementType = 2; + else + errMsg("unknown element type"); + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + void* ptr = &((*grid)[0]); +# if FLOATINGPOINT_PRECISION!=1 + // always write float values, even if compiled with double precision... + Grid<T> temp(grid->getParent()); + // "misuse" temp grid as storage for floating point values (we have double, so it will always fit) + //ptr = &(temp[0]); + //float* ptrf = (float*)ptr; + convertDoubleAndWrite( *grid, &(temp[0]), gzf, head); +# endif + gzwrite(gzf, &head, sizeof(UniHeader)); + gzwrite(gzf, ptr, sizeof(T)*head.dimX*head.dimY*head.dimZ); + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +}; + +// grid conversion functions for double precision +template <class T> +void convertFloatGridToDouble(Grid<T>& grid, void* ptr, int bytesPerElement) { + errMsg("unknown type, not yet supported"); +} + +template <> +void convertFloatGridToDouble<int>(Grid<int>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(int), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(int) ); + // easy, nothing to do for ints + memcpy(&(grid[0]), ptr, sizeof(int) * grid.getSizeX()*grid.getSizeY()*grid.getSizeZ() ); +} + +template <> +void convertFloatGridToDouble<double>(Grid<double>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(float), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(float) ); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i,++ptrf) { + grid[i] = (double)(*ptrf); + } +} + +template <> +void convertFloatGridToDouble<Vec3>(Grid<Vec3>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(Vector3D<float>), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(Vector3D<float>) ); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i) { + Vec3 v; + for(int c=0; c<3; ++c) { v[c] = double(*ptrf); ptrf++; } + grid[i] = v; + } +} + +// make sure compatible grid types dont lead to errors... +static int unifyGridType(int type) { + // real <> levelset + if(type & GridBase::TypeReal) type |= GridBase::TypeLevelset; + if(type & GridBase::TypeLevelset) type |= GridBase::TypeReal; + // vec3 <> mac + if(type & GridBase::TypeVec3) type |= GridBase::TypeMAC; + if(type & GridBase::TypeMAC) type |= GridBase::TypeVec3; + return type; +} + +template <class T> +void readGridUni(const string& name, Grid<T>* grid) { + cout << "reading grid " << grid->getName() << " from uni file " << name << endl; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "DDF2")) { + // legacy file format + UniLegacyHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniLegacyHeader)) == sizeof(UniLegacyHeader), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match"); + assertMsg (head.bytesPerElement * head.elements == sizeof(T), "grid type doesn't match"); + // skip flags + int numEl = head.dimX*head.dimY*head.dimZ; + gzseek(gzf, numEl, SEEK_CUR); + // actual grid read + gzread(gzf, &((*grid)[0]), sizeof(T)*numEl); + } + else if (!strcmp(ID, "MNT1")) { + // legacy file format 2 + UniLegacyHeader2 head; + assertMsg (gzread(gzf, &head, sizeof(UniLegacyHeader2)) == sizeof(UniLegacyHeader2), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match, "<< Vec3(head.dimX,head.dimY,head.dimZ)<<" vs "<< grid->getSize() ); + assertMsg (head.gridType == grid->getType(), "grid type doesn't match "<< head.gridType<<" vs "<< grid->getType() ); + assertMsg (head.bytesPerElement == sizeof(T), "grid element size doesn't match "<< head.bytesPerElement <<" vs "<< sizeof(T) ); + gzread(gzf, &((*grid)[0]), sizeof(T)*head.dimX*head.dimY*head.dimZ); + } + else if (!strcmp(ID, "MNT2")) { + // current file format + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match, "<< Vec3(head.dimX,head.dimY,head.dimZ)<<" vs "<< grid->getSize() ); + assertMsg ( unifyGridType(head.gridType)==unifyGridType(grid->getType()) , "grid type doesn't match "<< head.gridType<<" vs "<< grid->getType() ); +# if FLOATINGPOINT_PRECISION!=1 + // convert float to double + Grid<T> temp(grid->getParent()); + void* ptr = &(temp[0]); + gzread(gzf, ptr, sizeof(T)*head.dimX*head.dimY*head.dimZ); + convertFloatGridToDouble<T>(*grid, ptr, head.bytesPerElement); +# else + assertMsg (head.bytesPerElement == sizeof(T), "grid element size doesn't match "<< head.bytesPerElement <<" vs "<< sizeof(T) ); + gzread(gzf, &((*grid)[0]), sizeof(T)*head.dimX*head.dimY*head.dimZ); +# endif + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +}; + +template <class T> +void writeGridVol(const string& name, Grid<T>* grid) { + cout << "writing grid " << grid->getName() << " to vol file " << name << endl; + errMsg("Type not yet supported!"); +} + +struct volHeader { + char ID[3]; + char version; + int encoding; + int dimX, dimY, dimZ; + int channels; + Vec3 bboxMin, bboxMax; +}; + +template <> +void writeGridVol<Real>(const string& name, Grid<Real>* grid) { + cout << "writing real grid " << grid->getName() << " to vol file " << name << endl; + + volHeader header; + header.ID[0] = 'V'; + header.ID[1] = 'O'; + header.ID[2] = 'L'; + header.version = 3; + header.encoding = 1; // float32 precision + header.dimX = grid->getSizeX(); + header.dimY = grid->getSizeY(); + header.dimZ = grid->getSizeZ(); + header.channels = 1; // only 1 channel + header.bboxMin = Vec3(-0.5); + header.bboxMax = Vec3( 0.5); + + FILE* fp = fopen( name.c_str(), "wb" ); + if (fp == NULL) { + errMsg("Cannot open '" << name << "'"); + return; + } + + fwrite( &header, sizeof(volHeader), 1, fp ); + +# if FLOATINGPOINT_PRECISION==1 + // for float, write one big chunk + fwrite( &(*grid)[0], sizeof(float), grid->getSizeX()*grid->getSizeY()*grid->getSizeZ(), fp ); +# else + // explicitly convert each entry to float - we might have double precision in mantaflow + FOR_IDX(*grid) { + float value = (*grid)[idx]; + fwrite( &value, sizeof(float), 1, fp ); + } +# endif + + fclose(fp); +}; + + +//***************************************************************************** +// particle data +//***************************************************************************** + +//! in line with grid uni header +typedef struct { + int dim; // number of partilces + int dimX, dimY, dimZ; // underlying solver resolution (all data in local coordinates!) + int elementType, bytesPerElement; // type id and byte size + char info[256]; // mantaflow build information + unsigned long timestamp; // creation time +} UniPartHeader; + +template <class T> +void writeParticlesUni(const std::string& name, BasicParticleSystem* parts ) { + cout << "writing particles " << parts->getName() << " to uni file " << name << endl; + +# if NO_ZLIB!=1 + char ID[5] = "PB02"; + UniPartHeader head; + head.dim = parts->size(); + Vec3i gridSize = parts->getParent()->getGridSize(); + head.dimX = gridSize.x; + head.dimY = gridSize.y; + head.dimZ = gridSize.z; + head.bytesPerElement = sizeof(T); + head.elementType = 0; // 0 for base data + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + gzwrite(gzf, &head, sizeof(UniPartHeader)); + gzwrite(gzf, &(parts->getData()[0]), sizeof(T)*head.dim); + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +}; + +template <class T> +void readParticlesUni(const std::string& name, BasicParticleSystem* parts ) { + cout << "reading particles " << parts->getName() << " from uni file " << name << endl; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "PB01")) { + errMsg("particle uni file format v01 not supported anymore"); + } else if (!strcmp(ID, "PB02")) { + // current file format + UniPartHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniPartHeader)) == sizeof(UniPartHeader), "can't read file, no header present"); + assertMsg ( ((head.bytesPerElement == sizeof(T)) && (head.elementType==0) ), "particle type doesn't match"); + + // re-allocate all data + parts->resizeAll( head.dim ); + + assertMsg (head.dim == parts->size() , "particle size doesn't match"); + int bytes = sizeof(T)*head.dim; + int readBytes = gzread(gzf, &(parts->getData()[0]), sizeof(T)*head.dim); + assertMsg(bytes==readBytes, "can't read uni file, stream length does not match, "<<bytes<<" vs "<<readBytes ); + + parts->transformPositions( Vec3i(head.dimX,head.dimY,head.dimZ), parts->getParent()->getGridSize() ); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +}; + +template <class T> +void writePdataUni(const std::string& name, ParticleDataImpl<T>* pdata ) { + cout << "writing particle data " << pdata->getName() << " to uni file " << name << endl; + +# if NO_ZLIB!=1 + char ID[5] = "PD01"; + UniPartHeader head; + head.dim = pdata->size(); + head.bytesPerElement = sizeof(T); + head.elementType = 1; // 1 for particle data, todo - add sub types? + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + gzwrite(gzf, &head, sizeof(UniPartHeader)); + gzwrite(gzf, &(pdata->get(0)), sizeof(T)*head.dim); + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +}; + +template <class T> +void readPdataUni(const std::string& name, ParticleDataImpl<T>* pdata ) { + cout << "reading particle data " << pdata->getName() << " from uni file " << name << endl; + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "PD01")) { + UniPartHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniPartHeader)) == sizeof(UniPartHeader), "can't read file, no header present"); + assertMsg ( ((head.bytesPerElement == sizeof(T)) && (head.elementType==1) ), "pdata type doesn't match"); + assertMsg (head.dim == pdata->size() , "pdata size doesn't match"); + int bytes = sizeof(T)*head.dim; + int readBytes = gzread(gzf, &(pdata->get(0)), sizeof(T)*head.dim); + assertMsg(bytes==readBytes, "can't read uni file, stream length does not match, "<<bytes<<" vs "<<readBytes ); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +// explicit instantiation +template void writeGridRaw<int> (const string& name, Grid<int>* grid); +template void writeGridRaw<Real>(const string& name, Grid<Real>* grid); +template void writeGridRaw<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridUni<int> (const string& name, Grid<int>* grid); +template void writeGridUni<Real>(const string& name, Grid<Real>* grid); +template void writeGridUni<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridVol<int> (const string& name, Grid<int>* grid); +template void writeGridVol<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridTxt<int> (const string& name, Grid<int>* grid); +template void writeGridTxt<Real>(const string& name, Grid<Real>* grid); +template void writeGridTxt<Vec3>(const string& name, Grid<Vec3>* grid); +template void readGridRaw<int> (const string& name, Grid<int>* grid); +template void readGridRaw<Real> (const string& name, Grid<Real>* grid); +template void readGridRaw<Vec3> (const string& name, Grid<Vec3>* grid); +template void readGridUni<int> (const string& name, Grid<int>* grid); +template void readGridUni<Real> (const string& name, Grid<Real>* grid); +template void readGridUni<Vec3> (const string& name, Grid<Vec3>* grid); + +template void writeParticlesUni<BasicParticleData>(const std::string& name, BasicParticleSystem* parts ); +template void readParticlesUni<BasicParticleData> (const std::string& name, BasicParticleSystem* parts ); + +template void writePdataUni<int> (const std::string& name, ParticleDataImpl<int>* pdata ); +template void writePdataUni<Real>(const std::string& name, ParticleDataImpl<Real>* pdata ); +template void writePdataUni<Vec3>(const std::string& name, ParticleDataImpl<Vec3>* pdata ); +template void readPdataUni<int> (const std::string& name, ParticleDataImpl<int>* pdata ); +template void readPdataUni<Real> (const std::string& name, ParticleDataImpl<Real>* pdata ); +template void readPdataUni<Vec3> (const std::string& name, ParticleDataImpl<Vec3>* pdata ); + +#if ENABLE_GRID_TEST_DATATYPE==1 +// dummy functions for test datatype - not really supported right now! +// but we need some function body for linking +template<> void writeGridRaw<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridUni<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridVol<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridTxt<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void readGridRaw<nbVector> (const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void readGridUni<nbVector> (const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +#endif // ENABLE_GRID_TEST_DATATYPE + + +} //namespace diff --git a/source/blender/python/manta_full/source/fileio.h b/source/blender/python/manta_full/source/fileio.h new file mode 100644 index 00000000000..26c206629ac --- /dev/null +++ b/source/blender/python/manta_full/source/fileio.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Loading and writing grids and meshes to disk + * + ******************************************************************************/ + +#ifndef _FILEIO_H +#define _FILEIO_H + +#include <string> + +namespace Manta { + +// forward decl. +class Mesh; +class FlagGrid; +template<class T> class Grid; +class BasicParticleSystem; +template<class T> class ParticleDataImpl; + +void writeObjFile(const std::string& name, Mesh* mesh); +void writeBobjFile(const std::string& name, Mesh* mesh); +void readObjFile(const std::string& name, Mesh* mesh, bool append); + +template<class T> void writeGridRaw(const std::string& name, Grid<T>* grid); +template<class T> void writeGridUni(const std::string& name, Grid<T>* grid); +template<class T> void writeGridVol(const std::string& name, Grid<T>* grid); +template<class T> void writeGridTxt(const std::string& name, Grid<T>* grid); + +template<class T> void readGridUni(const std::string& name, Grid<T>* grid); +template<class T> void readGridRaw(const std::string& name, Grid<T>* grid); + +template <class T> void writeParticlesUni(const std::string& name, BasicParticleSystem* parts ); +template <class T> void readParticlesUni (const std::string& name, BasicParticleSystem* parts ); + +template <class T> void writePdataUni(const std::string& name, ParticleDataImpl<T>* pdata ); +template <class T> void readPdataUni (const std::string& name, ParticleDataImpl<T>* pdata ); + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/flip.cpp b/source/blender/python/manta_full/source/flip.cpp new file mode 100644 index 00000000000..56046349a28 --- /dev/null +++ b/source/blender/python/manta_full/source/flip.cpp @@ -0,0 +1,487 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * FLIP (fluid implicit particles) + * for use with particle data fields + * + ******************************************************************************/ + +#include "particle.h" +#include "grid.h" +#include "randomstream.h" +#include "levelset.h" + +using namespace std; +namespace Manta { + + + + +// init + +PYTHON void sampleFlagsWithParticles( FlagGrid& flags, BasicParticleSystem& parts, + int discretization, Real randomness ) +{ + bool is3D = flags.is3D(); + Real jlen = randomness / discretization; + Vec3 disp (1.0 / discretization, 1.0 / discretization, 1.0/discretization); + RandomStream mRand(9832); + + //clear(); + + FOR_IJK_BND(flags, 0) { + if ( flags.isObstacle(i,j,k) ) continue; + if ( flags.isFluid(i,j,k) ) { + Vec3 pos (i,j,k); + for (int dk=0; dk<(is3D ? discretization : 1); dk++) + for (int dj=0; dj<discretization; dj++) + for (int di=0; di<discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5+di, 0.5+dj, 0.5+dk); + subpos += jlen * (Vec3(1,1,1) - 2.0 * mRand.getVec3()); + if(!is3D) subpos[2] = 0.5; + parts.add( BasicParticleData(subpos) ); + } + } + } +} + +PYTHON void sampleLevelsetWithParticles( LevelsetGrid& phi, FlagGrid& flags, BasicParticleSystem& parts, + int discretization, Real randomness ) +{ + bool is3D = phi.is3D(); + Real jlen = randomness / discretization; + Vec3 disp (1.0 / discretization, 1.0 / discretization, 1.0/discretization); + RandomStream mRand(9832); + + //clear(); + + FOR_IJK_BND(phi, 0) { + if ( flags.isObstacle(i,j,k) ) continue; + if ( phi(i,j,k) < 1.733 ) { + Vec3 pos (i,j,k); + for (int dk=0; dk<(is3D ? discretization : 1); dk++) + for (int dj=0; dj<discretization; dj++) + for (int di=0; di<discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5+di, 0.5+dj, 0.5+dk); + subpos += jlen * (Vec3(1,1,1) - 2.0 * mRand.getVec3()); + if(!is3D) subpos[2] = 0.5; + if( phi.getInterpolated(subpos) > 0. ) continue; + parts.add( BasicParticleData(subpos) ); + } + } + } +} + +PYTHON void markFluidCells(BasicParticleSystem& parts, FlagGrid& flags) { + // remove all fluid cells + FOR_IJK(flags) { + if (flags.isFluid(i,j,k)) { + flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid; + } + } + + // mark all particles in flaggrid as fluid + for(int idx=0;idx<parts.size();idx++) { + if (!parts.isActive(idx)) continue; + Vec3i p = toVec3i( parts.getPos(idx) ); + if (flags.isInBounds(p) && flags.isEmpty(p)) + flags(p) = (flags(p) | FlagGrid::TypeFluid) & ~FlagGrid::TypeEmpty; + } +} + +// for testing purposes only... +PYTHON void testInitGridWithPos(Grid<Real>& grid) { + FOR_IJK(grid) { grid(i,j,k) = norm( Vec3(i,j,k) ); } +} + + +//! helper to calculate particle radius factor to cover the diagonal of a cell in 2d/3d +inline Real calculateRadiusFactor(Grid<Real>& grid, Real factor) { + return (grid.is3D() ? sqrt(3.) : sqrt(2.) ) * (factor+.01); // note, a 1% safety factor is added here +} + +//! re-sample particles based on an input levelset +PYTHON void adjustNumber( BasicParticleSystem& parts, MACGrid& vel, FlagGrid& flags, + int minParticles, int maxParticles, LevelsetGrid& phi, Real radiusFactor=1. ) +{ + // which levelset to use as threshold + const Real SURFACE_LS = -1.0 * calculateRadiusFactor(phi, radiusFactor); + Grid<int> tmp( vel.getParent() ); + std::ostringstream out; + + // count particles in cells, and delete excess particles + for (int idx=0; idx<(int)parts.size(); idx++) { + if (parts.isActive(idx)) { + Vec3i p = toVec3i( parts.getPos(idx) ); + if (!tmp.isInBounds(p) ) { + parts.kill(idx); // out of domain, remove + continue; + } + int num = tmp(p); + + bool atSurface = false; + Real phiv = phi.getInterpolated( parts.getPos(idx) ); + if (phiv > SURFACE_LS) atSurface = true; + + // dont delete particles in non fluid cells here, the particles are "always right" + if ( num > maxParticles && (!atSurface) ) { + parts.kill(idx); + } else { + tmp(p) = num+1; + } + } + } + + // seed new particles + RandomStream mRand(9832); + FOR_IJK(tmp) { + int cnt = tmp(i,j,k); + + // skip cells near surface + if (phi(i,j,k) > SURFACE_LS) continue; + + if (flags.isFluid(i,j,k) && cnt < minParticles) { + for (int m=cnt; m < minParticles; m++) { + Vec3 pos = Vec3(i,j,k) + mRand.getVec3(); + //Vec3 pos (i + 0.5, j + 0.5, k + 0.5); // cell center + parts.addBuffered( pos ); + } + } + } + + parts.doCompress(); + parts.insertBufferedParticles(); +} + +// simple and slow helper conversion to show contents of int grids like a real grid in the ui +// (use eg to quickly display contents of the particle-index grid) +PYTHON void debugIntToReal( Grid<int>& source, Grid<Real>& dest, Real factor=1. ) +{ + FOR_IJK( source ) { dest(i,j,k) = (Real)source(i,j,k) * factor; } +} + +// build a grid that contains indices for a particle system +// the particles in a cell i,j,k are particles[index(i,j,k)] to particles[index(i+1,j,k)-1] +// (ie, particles[index(i+1,j,k)] alreadu belongs to cell i+1,j,k) +PYTHON void gridParticleIndex( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, + FlagGrid& flags, Grid<int>& index, Grid<int>* counter=NULL) +{ + bool delCounter = false; + if(!counter) { counter = new Grid<int>( flags.getParent() ); delCounter=true; } + else { counter->clear(); } + + // count particles in cells, and delete excess particles + index.clear(); + int inactive = 0; + for (int idx=0; idx<(int)parts.size(); idx++) { + if (parts.isActive(idx)) { + // check index for validity... + Vec3i p = toVec3i( parts.getPos(idx) ); + if (! index.isInBounds(p)) { inactive++; continue; } + + index(p)++; + } else { + inactive++; + } + } + + // note - this one might be smaller... + indexSys.resize( parts.size()-inactive ); + + // convert per cell number to continuous index + int idx=0; + FOR_IJK( index ) { + int num = index(i,j,k); + index(i,j,k) = idx; + idx += num; + } + + // add particles to indexed array, we still need a per cell particle counter + for (int idx=0; idx<(int)parts.size(); idx++) { + if (!parts.isActive(idx)) continue; + Vec3i p = toVec3i( parts.getPos(idx) ); + if (! index.isInBounds(p)) { continue; } + + // initialize position and index into original array + //indexSys[ index(p)+(*counter)(p) ].pos = parts[idx].pos; + indexSys[ index(p)+(*counter)(p) ].sourceIndex = idx; + (*counter)(p)++; + } + + if(delCounter) delete counter; +} + +KERNEL +void ComputeUnionLevelsetPindex(Grid<int>& index, BasicParticleSystem& parts, ParticleIndexSystem& indexSys, + LevelsetGrid& phi, Real radius=1.) +{ + const Vec3 gridPos = Vec3(i,j,k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.732; // outside + + int r = int(radius) + 1; + int rZ = phi.is3D() ? r : 0; + for(int zj=k-rZ; zj<=k+rZ; zj++) + for(int yj=j-r ; yj<=j+r ; yj++) + for(int xj=i-r ; xj<=i+r ; xj++) { + if (!phi.isInBounds(Vec3i(xj,yj,zj))) continue; + + // note, for the particle indices in indexSys the access is periodic (ie, dont skip for eg inBounds(sx,10,10) + int isysIdxS = phi.index(xj,yj,zj); + int pStart = index(isysIdxS), pEnd=0; + if(phi.isInBounds(isysIdxS+1)) pEnd = index(isysIdxS+1); + else pEnd = indexSys.size(); + + // now loop over particles in cell + for(int p=pStart; p<pEnd; ++p) { + const int psrc = indexSys[p].sourceIndex; + const Vec3 pos = parts[psrc].pos; + phiv = std::min( phiv , fabs( norm(gridPos-pos) )-radius ); + } + } + phi(i,j,k) = phiv; +} + +PYTHON void unionParticleLevelset( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, + FlagGrid& flags, Grid<int>& index, LevelsetGrid& phi, Real radiusFactor=1. ) +{ + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + // no reset of phi necessary here + ComputeUnionLevelsetPindex(index, parts, indexSys, phi, radius); +} + + +KERNEL +void ComputeAveragedLevelsetWeight(BasicParticleSystem& parts, + Grid<int>& index, ParticleIndexSystem& indexSys, + LevelsetGrid& phi, Real radius=1.) +{ + const Vec3 gridPos = Vec3(i,j,k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.732; // outside + + // loop over neighborhood, similar to ComputeUnionLevelsetPindex + const Real sradiusInv = 1. / (4. * radius * radius) ; + int r = int(1. * radius) + 1; + int rZ = phi.is3D() ? r : 0; + // accumulators + Real wacc = 0.; + Vec3 pacc = Vec3(0.); + Real racc = 0.; + + for(int zj=k-rZ; zj<=k+rZ; zj++) + for(int yj=j-r ; yj<=j+r ; yj++) + for(int xj=i-r ; xj<=i+r ; xj++) { + if (! phi.isInBounds(Vec3i(xj,yj,zj)) ) continue; + + int isysIdxS = phi.index(xj,yj,zj); + int pStart = index(isysIdxS), pEnd=0; + if(phi.isInBounds(isysIdxS+1)) pEnd = index(isysIdxS+1); + else pEnd = indexSys.size(); + for(int p=pStart; p<pEnd; ++p) { + int psrc = indexSys[p].sourceIndex; + Vec3 pos = parts[psrc].pos; + Real s = normSquare(gridPos-pos) * sradiusInv; + Real w = std::max(0., cubed(1.-s) ); + wacc += w; + racc += radius * w; + pacc += pos * w; + } + } + + if(wacc > VECTOR_EPSILON) { + racc /= wacc; + pacc /= wacc; + phiv = fabs( norm(gridPos-pacc) )-racc; + } + phi(i,j,k) = phiv; +} + +// smoothing, and +KERNEL(bnd=1) template<class T> +void knSmoothGrid(Grid<T>& me, Grid<T>& tmp, Real factor) { + T val = me(i,j,k) + + me(i+1,j,k) + me(i-1,j,k) + + me(i,j+1,k) + me(i,j-1,k) ; + if(me.is3D()) { + val += me(i,j,k+1) + me(i,j,k-1); + } + tmp(i,j,k) = val * factor; +} + +KERNEL(bnd=1) template<class T> +void knSmoothGridNeg(Grid<T>& me, Grid<T>& tmp, Real factor) { + T val = me(i,j,k) + + me(i+1,j,k) + me(i-1,j,k) + + me(i,j+1,k) + me(i,j-1,k) ; + if(me.is3D()) { + val += me(i,j,k+1) + me(i,j,k-1); + } + val *= factor; + if(val<tmp(i,j,k)) tmp(i,j,k) = val; + else tmp(i,j,k) = me(i,j,k); +} + + +PYTHON void averagedParticleLevelset( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, + FlagGrid& flags, Grid<int>& index, LevelsetGrid& phi, Real radiusFactor=1. , + int smoothen=1 , int smoothenNeg=1 ) +{ + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + ComputeAveragedLevelsetWeight(parts, index, indexSys, phi, radius); + + // post-process level-set + for(int i=0; i<smoothen; ++i) { + LevelsetGrid tmp(flags.getParent()); + knSmoothGrid<Real>(phi,tmp, 1./(phi.is3D() ? 7. : 5.) ); + phi.swap(tmp); + } + for(int i=0; i<smoothenNeg; ++i) { + LevelsetGrid tmp(flags.getParent()); + knSmoothGridNeg<Real>(phi,tmp, 1./(phi.is3D() ? 7. : 5.) ); + phi.swap(tmp); + } + // NT_DEBUG , todo copy border +} + + + + +//****************************************************************************** +// grid interpolation functions + +KERNEL(idx) template<class T> +void knSafeDivReal(Grid<T>& me, const Grid<Real>& other, Real cutoff=VECTOR_EPSILON) { + if(other[idx]<cutoff) { + me[idx] = 0.; + } else { + T div( other[idx] ); + me[idx] = safeDivide(me[idx], div ); + } +} + +// Set velocities on the grid from the particle system + +KERNEL(idx) +void knStompVec3PerComponent(Grid<Vec3>& grid, Real threshold) { + if(grid[idx][0] < threshold) grid[idx][0] = 0.; + if(grid[idx][1] < threshold) grid[idx][1] = 0.; + if(grid[idx][2] < threshold) grid[idx][2] = 0.; +} + +KERNEL(pts, single) +void knMapLinearVec3ToMACGrid( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, Grid<Vec3>& tmp, + ParticleDataImpl<Vec3>& pvel ) +{ + unusedParameter(flags); + if (!p.isActive(idx)) return; + vel.setInterpolated( p[idx].pos, pvel[idx], &tmp[0] ); +} + +// optionally , this function can use an existing vec3 grid to store the weights +// this is useful in combination with the simple extrapolation function +PYTHON void mapPartsToMAC( FlagGrid& flags, MACGrid& vel , MACGrid& velOld , + BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel , Grid<Vec3>* weight=NULL ) +{ + // interpol -> grid. tmpgrid for particle contribution weights + bool freeTmp = false; + if(!weight) { + weight = new Grid<Vec3>(flags.getParent()); + freeTmp = true; + } else { + weight->clear(); // make sure we start with a zero grid! + } + vel.clear(); + knMapLinearVec3ToMACGrid( parts, flags, vel, *weight, partVel ); + + // stomp small values in weight to zero to prevent roundoff errors + knStompVec3PerComponent( *weight, VECTOR_EPSILON ); + vel.safeDivide(*weight); + + // store original state + velOld = vel; + if(freeTmp) delete weight; +} + + +KERNEL(pts, single) template<class T> +void knMapLinear( BasicParticleSystem& p, FlagGrid& flags, Grid<T>& target, Grid<Real>& gtmp, + ParticleDataImpl<T>& psource ) +{ + unusedParameter(flags); + if (!p.isActive(idx)) return; + target.setInterpolated( p[idx].pos, psource[idx], gtmp ); +} +template<class T> +void mapLinearRealHelper( FlagGrid& flags, Grid<T>& target , + BasicParticleSystem& parts , ParticleDataImpl<T>& source ) +{ + Grid<Real> tmp(flags.getParent()); + target.clear(); + knMapLinear<T>( parts, flags, target, tmp, source ); + knSafeDivReal<T>( target, tmp ); +} + +PYTHON void mapPartsToGrid ( FlagGrid& flags, Grid<Real>& target , BasicParticleSystem& parts , ParticleDataImpl<Real>& source ) { + mapLinearRealHelper<Real>(flags,target,parts,source); +} +PYTHON void mapPartsToGridVec3( FlagGrid& flags, Grid<Vec3>& target , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& source ) { + mapLinearRealHelper<Vec3>(flags,target,parts,source); +} +// integers need "max" mode, not yet implemented +//PYTHON void mapPartsToGridInt ( FlagGrid& flags, Grid<int >& target , BasicParticleSystem& parts , ParticleDataImpl<int >& source ) { +// mapLinearRealHelper<int >(flags,target,parts,source); +//} + +KERNEL(pts) template<class T> +void knMapFromGrid( BasicParticleSystem& p, Grid<T>& gsrc, ParticleDataImpl<T>& target ) +{ + if (!p.isActive(idx)) return; + target[idx] = gsrc.getInterpolated( p[idx].pos ); +} +PYTHON void mapGridToParts ( Grid<Real>& source , BasicParticleSystem& parts , ParticleDataImpl<Real>& target ) { + knMapFromGrid<Real>(parts, source, target); +} +PYTHON void mapGridToPartsVec3( Grid<Vec3>& source , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& target ) { + knMapFromGrid<Vec3>(parts, source, target); +} + + +// Get velocities from grid + +KERNEL(pts) +void knMapLinearMACGridToVec3_PIC( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, ParticleDataImpl<Vec3>& pvel ) +{ + if (!p.isActive(idx)) return; + // pure PIC + pvel[idx] = vel.getInterpolated( p[idx].pos ); +} +PYTHON void mapMACToParts(FlagGrid& flags, MACGrid& vel , + BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel ) { + knMapLinearMACGridToVec3_PIC( parts, flags, vel, partVel ); +} + +// with flip delta interpolation +KERNEL(pts) +void knMapLinearMACGridToVec3_FLIP( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, MACGrid& oldVel, ParticleDataImpl<Vec3>& pvel , Real flipRatio) +{ + if (!p.isActive(idx)) return; + Vec3 v = vel.getInterpolated(p[idx].pos); + Vec3 delta = v - oldVel.getInterpolated(p[idx].pos); + pvel[idx] = flipRatio * (pvel[idx] + delta) + (1.0 - flipRatio) * v; +} + +PYTHON void flipVelocityUpdate(FlagGrid& flags, MACGrid& vel , MACGrid& velOld , + BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel , Real flipRatio ) { + knMapLinearMACGridToVec3_FLIP( parts, flags, vel, velOld, partVel, flipRatio ); +} + + +} // namespace + diff --git a/source/blender/python/manta_full/source/flip.h b/source/blender/python/manta_full/source/flip.h new file mode 100644 index 00000000000..c427a97df24 --- /dev/null +++ b/source/blender/python/manta_full/source/flip.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * FLIP (fluid implicit particles) + * + ******************************************************************************/ + +#ifndef _FLIP_H +#define _FLIP_H + +namespace Manta { + +// todo remove this file, not needed anymore... + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/fluidsolver.cpp b/source/blender/python/manta_full/source/fluidsolver.cpp new file mode 100644 index 00000000000..1e1557750b6 --- /dev/null +++ b/source/blender/python/manta_full/source/fluidsolver.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main class for the fluid solver + * + ******************************************************************************/ + +#include "fluidsolver.h" +#include "grid.h" +#include <sstream> +#include <fstream> + +using namespace std; +namespace Manta { + +#ifdef GUI + // defined in qtmain.cpp + extern void updateQtGui(bool full, int frame, const std::string& curPlugin); +#else + inline void updateQtGui(bool full, int frame, const std::string& curPlugin) {} +#endif + +//****************************************************************************** +// Gridstorage-related members + +template<class T> +void FluidSolver::GridStorage<T>::free() { + if (used != 0) + errMsg("can't clean grid cache, some grids are still in use"); + for(size_t i = 0; i<grids.size(); i++) + delete[] grids[i]; + grids.clear(); +} +template<class T> +T* FluidSolver::GridStorage<T>::get(Vec3i size) { + if ((int)grids.size() <= used) { + grids.push_back(new T[size.x * size.y * size.z]); + } + if (used > 200) + errMsg("too many temp grids used -- are they released properly ?"); + return grids[used++]; +} +template<class T> +void FluidSolver::GridStorage<T>::release(T* ptr) { + // rewrite pointer, as it may have changed due to swap operations + used--; + if (used < 0) + errMsg("temp grid inconsistency"); + grids[used] = ptr; +} + +template<> int* FluidSolver::getGridPointer<int>() { + return mGridsInt.get(mGridSize); +} +template<> Real* FluidSolver::getGridPointer<Real>() { + return mGridsReal.get(mGridSize); +} +template<> Vec3* FluidSolver::getGridPointer<Vec3>() { + return mGridsVec.get(mGridSize); +} +template<> void FluidSolver::freeGridPointer<int>(int *ptr) { + mGridsInt.release(ptr); +} +template<> void FluidSolver::freeGridPointer<Real>(Real* ptr) { + mGridsReal.release(ptr); +} +template<> void FluidSolver::freeGridPointer<Vec3>(Vec3* ptr) { + mGridsVec.release(ptr); +} + +void FluidSolver::pluginStart(const string& name) { + mLastPlugin = name; + mPluginTimer.get(); +} + +void FluidSolver::pluginStop(const string& name) { + if (mLastPlugin == name && name != "FluidSolver::step") { + MuTime diff = mPluginTimer.update(); + mTimings.push_back(pair<string,MuTime>(name, diff)); + } +} + +//****************************************************************************** +// FluidSolver members + +FluidSolver::FluidSolver(Vec3i gridsize, int dim) + : PbClass(this), mDt(1.0), mGridSize(gridsize), mDim(dim), mTimeTotal(0.), mScale(1.0), mFrame(0) +{ + assertMsg(dim==2 || dim==3, "Can only create 2D and 3D solvers"); + assertMsg(dim!=2 || gridsize.z == 1, "Trying to create 2D solver with size.z != 1"); +} + +FluidSolver::~FluidSolver() { + mGridsInt.free(); + mGridsReal.free(); + mGridsVec.free(); +} + +PbClass* FluidSolver::create(PbType t, PbTypeVec T, const string& name) { + _args.add("nocheck",true); + if (t.str() == "") + errMsg("Need to specify object type. Use e.g. Solver.create(FlagGrid, ...) or Solver.create(type=FlagGrid, ...)"); + + return PbClass::createPyObject(t.str() + T.str(), name, _args, this); +} + +void FluidSolver::step() { + mTimeTotal += mDt; + mFrame++; + updateQtGui(true, mFrame, "FluidSolver::step"); + + // update timings + for(size_t i=0;i<mTimings.size(); i++) { + const string name = mTimings[i].first; + if (mTimingsTotal.find(name) == mTimingsTotal.end()) + mTimingsTotal[name].second.clear(); + mTimingsTotal[name].first++; + mTimingsTotal[name].second+=mTimings[i].second; + } + mTimings.clear(); +} + +void FluidSolver::printTimings() { + MuTime total; + total.clear(); + for(size_t i=0; i<mTimings.size(); i++) + total += mTimings[i].second; + + printf("\n-- STEP %d -----------------------------\n", mFrame); + for(size_t i=0; i<mTimings.size(); i++) + printf("[%4.1f%%] %s (%s)\n", 100.0*((Real)mTimings[i].second.time / (Real)total.time), mTimings[i].first.c_str(), mTimings[i].second.toString().c_str()); + printf("----------------------------------------\n"); + printf("Total : %s\n\n", total.toString().c_str()); +} + +void FluidSolver::printMemInfo() { + std::ostringstream msg; + msg << "Allocated grids: int " << mGridsInt.used <<"/"<< mGridsInt.grids.size() <<", "; + msg << "real "<< mGridsReal.used <<"/"<< mGridsReal.grids.size() <<", "; + msg << "vec3 "<< mGridsVec.used <<"/"<< mGridsVec.grids.size() <<". "; + printf("%s\n", msg.str().c_str() ); +} + +PYTHON() void printBuildInfo() { + debMsg( "Build info: "<<buildInfoString().c_str()<<" ",1); +} + +void FluidSolver::saveMeanTimings(string filename) { + ofstream ofs(filename.c_str()); + if (!ofs.good()) + errMsg("can't open " + filename + " as timing log"); + ofs << "Mean timings of " << mFrame << " steps :" <<endl <<endl; + MuTime total; + total.clear(); + for(map<string, pair<int,MuTime> >::iterator it=mTimingsTotal.begin(); it!=mTimingsTotal.end(); it++) { + total += it->second.second; + } + for(map<string, pair<int,MuTime> >::iterator it=mTimingsTotal.begin(); it!=mTimingsTotal.end(); it++) { + ofs << it->first << ": " << it->second.second / it->second.first << endl; + } + ofs << endl << "Total : " << total << " (mean " << total/mFrame << ")" << endl; + ofs.close(); +} + +} + diff --git a/source/blender/python/manta_full/source/fluidsolver.h b/source/blender/python/manta_full/source/fluidsolver.h new file mode 100644 index 00000000000..f3fd9e29e7c --- /dev/null +++ b/source/blender/python/manta_full/source/fluidsolver.h @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main class for the fluid solver + * + ******************************************************************************/ + +#ifndef _FLUIDSOLVER_H +#define _FLUIDSOLVER_H + +#include "manta.h" +#include "vectorbase.h" +#include <vector> +#include <map> + +namespace Manta { + +//! Encodes grid size, timstep etc. +PYTHON(name=Solver) +class FluidSolver : public PbClass { +public: + PYTHON FluidSolver(Vec3i gridSize, int dim=3); + virtual ~FluidSolver(); + + // accessors + PYTHON Vec3i getGridSize() { return mGridSize; } + inline Real getDt() { return mDt; } + inline Real getTime() { return mTimeTotal; } + inline Real getDx() { return 1.0 / mGridSize.max(); } + inline Real getScale() { return mScale; } + //! Check dimensionality + inline bool is2D() const { return mDim==2; } + //! Check dimensionality + inline bool is3D() const { return mDim==3; } + + // Python callable methods + //! output performace statistics + PYTHON void printTimings(); + PYTHON void saveMeanTimings(std::string filename); + PYTHON void printMemInfo(); + + //! Advance the solver one timestep, update GUI if present + PYTHON void step(); + + //! create a object with the solver as its parent + PYTHON PbClass* create(PbType type, PbTypeVec T=PbTypeVec(),const std::string& name = ""); + + // temp grid and plugin stuff: you shouldn't call this manually + template<class T> T* getGridPointer(); + template<class T> void freeGridPointer(T* ptr); + void pluginStart(const std::string& name); + void pluginStop(const std::string& name); + + PYTHON(name=timestep) Real mDt; +protected: + //! subclass for managing grid memory + //! stored as a stack to allow fast allocation + template<class T> struct GridStorage { + GridStorage() : used(0) {} + T* get(Vec3i size); + void free(); + void release(T* ptr); + + std::vector<T*> grids; + int used; + }; + + Vec3i mGridSize; + const int mDim; + Real mTimeTotal, mScale; + int mFrame; + + GridStorage<int> mGridsInt; + GridStorage<Real> mGridsReal; + GridStorage<Vec3> mGridsVec; + + // for timing plugins + MuTime mPluginTimer; + std::string mLastPlugin; + std::vector<std::pair<std::string, MuTime> > mTimings; + std::map<std::string, std::pair<int,MuTime> > mTimingsTotal; +}; + +} + +#endif diff --git a/source/blender/python/manta_full/source/general.cpp b/source/blender/python/manta_full/source/general.cpp new file mode 100644 index 00000000000..e6218e677b1 --- /dev/null +++ b/source/blender/python/manta_full/source/general.cpp @@ -0,0 +1,128 @@ + /****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Globally used macros and functions + * + ******************************************************************************/ + +#include "general.h" +#if defined(WIN32) || defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX +#else +# include <sys/time.h> +//# include "hginfo.h" +#endif + +using namespace std; + +namespace Manta { + +int gDebugLevel = 1; + +void MuTime::get() { +#if defined(WIN32) || defined(_WIN32) + LARGE_INTEGER liTimerFrequency; + QueryPerformanceFrequency(&liTimerFrequency); + LARGE_INTEGER liLastTime; + QueryPerformanceCounter(&liLastTime); + time = (INT)( ((double)liLastTime.QuadPart / liTimerFrequency.QuadPart)*1000 ); +#else + struct timeval tv; + struct timezone tz; + tz.tz_minuteswest = 0; + tz.tz_dsttime = 0; + gettimeofday(&tv,&tz); + time = (tv.tv_sec*1000)+(tv.tv_usec/1000); +#endif +} + +MuTime MuTime::update() { + MuTime o = *this; + get(); + return *this - o; +} + +string MuTime::toString() { + stringstream ss; + ss << *this; + return ss.str(); +} + +ostream& operator<<(ostream& os, const MuTime& t) { + unsigned long ms = (unsigned long)( (double)t.time / (60.0*1000.0) ); + unsigned long ss = (unsigned long)( ((double)t.time / 1000.0) - ((double)ms*60.0) ); + int ps = (int)( ((double)t.time - (double)ss*1000.0)/1.0 ); + + if(ms>0) { + os << ms<<"m"<< ss<<"s" ; + } else { + if(ps>0) { + os << ss<<"."; + if(ps<10) { os <<"0"; } + if(ps<100) { os <<"0"; } + os <<ps<<"s" ; + } else { + os << ss<<"s" ; + } + } + return os; +} + +std::string buildInfoString() { + std::ostringstream infoStr; + infoStr << "mantaflow"; + // TODO , include hg branch info + + // os +#if defined(WIN32) || defined(_WIN32) + infoStr << " win"; +# endif +# ifdef __APPLE__ + infoStr << " mac"; +# endif +# ifdef LINUX + infoStr << " linux"; +# endif + + // 32/64 bit + if (sizeof(size_t) == 8) + infoStr << " 64bit"; + else + infoStr << " 32bit"; + + // fp precision +# if FLOATINGPOINT_PRECISION==2 + infoStr << " fp2"; +# else + infoStr << " fp1"; +# endif + + // other compile switches +# ifdef DEBUG + infoStr << " debug"; +# endif +# ifdef OPENMP + infoStr << " omp"; +# endif + + // repository info +//# ifndef MANTA_HG_VERSION +//# define MANTA_HG_VERSION "<unknown>" +//# endif +// infoStr << " hg "<< MANTA_HG_VERSION; +// +// infoStr << " from "<< __DATE__<<", "<<__TIME__; + return infoStr.str(); +} + +} // namespace diff --git a/source/blender/python/manta_full/source/general.h b/source/blender/python/manta_full/source/general.h new file mode 100644 index 00000000000..25ec2068ed0 --- /dev/null +++ b/source/blender/python/manta_full/source/general.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Globally used macros and functions + * + ******************************************************************************/ + +#ifndef _GENERAL_H +#define _GENERAL_H + +#include <iostream> +#include <sstream> +#include <cmath> + +namespace Manta { + +// activate debug mode if _DEBUG is defined (eg for windows) +#ifndef DEBUG +#ifdef _DEBUG +#define DEBUG 1 +#endif // _DEBUG +#endif // DEBUG + +// Standard exception +class Error : public std::exception +{ +public: + Error(const std::string& s) : mS(s) { +# ifdef DEBUG + // print error + std::cerr << "Aborting: "<< s <<" \n"; + // then force immedieate crash in debug mode + *(volatile int*)(0) = 1; +# endif + } + virtual ~Error() throw() {} + virtual const char* what() const throw() { return mS.c_str(); } +private: + std::string mS; +}; + +// mark unused parameter variables +#define unusedParameter(x) ((void)x) + +// Debug output functions and macros +extern int gDebugLevel; + +#define MSGSTREAM std::ostringstream msg; msg.precision(7); msg.width(9); +#define debMsg(mStr, level) if (_chklevel(level)) { MSGSTREAM; msg << mStr; std::cout << msg.str() << std::endl; } +inline bool _chklevel(int level=0) { return gDebugLevel >= level; } + +// error and assertation macros +#ifdef DEBUG +# define DEBUG_ONLY(a) a +#else +# define DEBUG_ONLY(a) +#endif +#define throwError(msg) { std::ostringstream __s; __s << msg << std::endl << "Error raised in " << __FILE__ << ":" << __LINE__; throw Manta::Error(__s.str()); } +#define errMsg(msg) throwError(msg); +#define assertMsg(cond,msg) if(!(cond)) throwError(msg) +#define assertDeb(cond,msg) DEBUG_ONLY( assertMsg(cond,msg) ) + +// template tricks +template<typename T> +struct remove_pointers { + typedef T type; +}; + +template<typename T> +struct remove_pointers<T*> { + typedef T type; +}; + +template<typename T> +struct remove_pointers<T&> { + typedef T type; +}; + +// Commonly used enums and types +//! Timing class for preformance measuring +struct MuTime { + MuTime() { get(); } + MuTime operator-(const MuTime& a) { MuTime b; b.time = time - a.time; return b; }; + MuTime operator+(const MuTime& a) { MuTime b; b.time = time + a.time; return b; }; + MuTime operator/(unsigned long a) { MuTime b; b.time = time / a; return b; }; + MuTime& operator+=(const MuTime& a) { time += a.time; return *this; } + MuTime& operator-=(const MuTime& a) { time -= a.time; return *this; } + MuTime& operator/=(unsigned long a) { time /= a; return *this; } + std::string toString(); + + void clear() { time = 0; } + void get(); + MuTime update(); + + unsigned long time; +}; +std::ostream& operator<< (std::ostream& os, const MuTime& t); + +//! generate a string with infos about the current mantaflow build +std::string buildInfoString(); + +// Some commonly used math helpers +template<class T> inline T square(T a) { + return a*a; +} +template<class T> inline T cubed(T a) { + return a*a; +} + +template<class T> inline T clamp(const T& val, const T& vmin, const T& vmax) { + if (val < vmin) return vmin; + if (val > vmax) return vmax; + return val; +} + +template<class T> inline T nmod(const T& a, const T& b); +template<> inline int nmod(const int& a, const int& b) { int c=a%b; return (c<0) ? (c+b) : c; } +template<> inline float nmod(const float& a, const float& b) { float c=std::fmod(a,b); return (c<0) ? (c+b) : c; } +template<> inline double nmod(const double& a, const double& b) { double c=std::fmod(a,b); return (c<0) ? (c+b) : c; } +template<class T> inline T safeDivide(const T& a, const T& b); +template<> inline int safeDivide<int>(const int &a, const int& b) { return (b) ? (a/b) : a; } +template<> inline float safeDivide<float>(const float &a, const float& b) { return (b) ? (a/b) : a; } +template<> inline double safeDivide<double>(const double &a, const double& b) { return (b) ? (a/b) : a; } + +inline bool c_isnan(float c) { + volatile float d=c; + return d != d; +} + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/grid.cpp b/source/blender/python/manta_full/source/grid.cpp new file mode 100644 index 00000000000..9bde755f3fd --- /dev/null +++ b/source/blender/python/manta_full/source/grid.cpp @@ -0,0 +1,445 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Grid representation + * + ******************************************************************************/ + +#include "grid.h" +#include "levelset.h" +#include "kernel.h" +#include <limits> +#include <sstream> +#include <cstring> +#include "fileio.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// GridBase members + +GridBase::GridBase (FluidSolver* parent) + : PbClass(parent), mType(TypeNone) +{ + checkParent(); + m3D = getParent()->is3D(); +} + +//****************************************************************************** +// Grid<T> members + +// helpers to set type +template<class T> inline GridBase::GridType typeList() { return GridBase::TypeNone; } +template<> inline GridBase::GridType typeList<Real>() { return GridBase::TypeReal; } +template<> inline GridBase::GridType typeList<int>() { return GridBase::TypeInt; } +template<> inline GridBase::GridType typeList<Vec3>() { return GridBase::TypeVec3; } + +template<class T> +Grid<T>::Grid(FluidSolver* parent, bool show) + : GridBase(parent) +{ + mType = typeList<T>(); + mSize = parent->getGridSize(); + mData = parent->getGridPointer<T>(); + + mStrideZ = parent->is2D() ? 0 : (mSize.x * mSize.y); + mDx = 1.0 / mSize.max(); + clear(); + setHidden(!show); +} + +template<class T> +Grid<T>::Grid(const Grid<T>& a) : GridBase(a.getParent()) { + mSize = a.mSize; + mType = a.mType; + mStrideZ = a.mStrideZ; + mDx = a.mDx; + FluidSolver *gp = a.getParent(); + mData = gp->getGridPointer<T>(); + memcpy(mData, a.mData, sizeof(T) * a.mSize.x * a.mSize.y * a.mSize.z); +} + +template<class T> +Grid<T>::~Grid() { + mParent->freeGridPointer<T>(mData); +} + +template<class T> +void Grid<T>::clear() { + memset(mData, 0, sizeof(T) * mSize.x * mSize.y * mSize.z); +} + +template<class T> +void Grid<T>::swap(Grid<T>& other) { + if (other.getSizeX() != getSizeX() || other.getSizeY() != getSizeY() || other.getSizeZ() != getSizeZ()) + errMsg("Grid::swap(): Grid dimensions mismatch."); + + T* dswap = other.mData; + other.mData = mData; + mData = dswap; +} + +template<class T> +void Grid<T>::load(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".raw") + readGridRaw(name, this); + else if (ext == ".uni") + readGridUni(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +template<class T> +void Grid<T>::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".raw") + writeGridRaw(name, this); + else if (ext == ".uni") + writeGridUni(name, this); + else if (ext == ".vol") + writeGridVol(name, this); + else if (ext == ".txt") + writeGridTxt(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +//****************************************************************************** +// Grid<T> operators + +//! Kernel: Compute min value of Real grid +KERNEL(idx, reduce=min) returns(Real minVal=std::numeric_limits<Real>::max()) +Real CompMinReal(Grid<Real>& val) { + if (val[idx] < minVal) + minVal = val[idx]; +} + +//! Kernel: Compute max value of Real grid +KERNEL(idx, reduce=max) returns(Real maxVal=-std::numeric_limits<Real>::max()) +Real CompMaxReal(Grid<Real>& val) { + if (val[idx] > maxVal) + maxVal = val[idx]; +} + +//! Kernel: Compute min value of int grid +KERNEL(idx, reduce=min) returns(int minVal=std::numeric_limits<int>::max()) +int CompMinInt(Grid<int>& val) { + if (val[idx] < minVal) + minVal = val[idx]; +} + +//! Kernel: Compute max value of int grid +KERNEL(idx, reduce=max) returns(int maxVal=-std::numeric_limits<int>::min()) +int CompMaxInt(Grid<int>& val) { + if (val[idx] > maxVal) + maxVal = val[idx]; +} + +//! Kernel: Compute min norm of vec grid +KERNEL(idx, reduce=min) returns(Real minVal=std::numeric_limits<Real>::max()) +Real CompMinVec(Grid<Vec3>& val) { + const Real s = normSquare(val[idx]); + if (s < minVal) + minVal = s; +} + +//! Kernel: Compute max norm of vec grid +KERNEL(idx, reduce=max) returns(Real maxVal=0) +Real CompMaxVec(Grid<Vec3>& val) { + const Real s = normSquare(val[idx]); + if (s > maxVal) + maxVal = s; +} + + +template<class T> Grid<T>& Grid<T>::safeDivide (const Grid<T>& a) { + gridSafeDiv<T> (*this, a); + return *this; +} +template<class T> Grid<T>& Grid<T>::operator= (const Grid<T>& a) { + assertMsg (a.mSize.x == mSize.x && a.mSize.y == mSize.y && a.mSize.z == mSize.z, "different grid resolutions "<<a.mSize<<" vs "<<this->mSize ); + memcpy(mData, a.mData, sizeof(T) * mSize.x * mSize.y * mSize.z); + mType = a.mType; // copy type marker + return *this; +} +/*template<class T> Grid<T>& Grid<T>::operator= (const T& a) { + FOR_IDX(*this) { mData[idx] = a; } + return *this; +}*/ + +PYTHON void setConstant (Grid<Real>& grid, Real value=0.) { gridSetConst<Real>(grid,value); } +PYTHON void setConstantVec3(Grid<Vec3>& grid, Vec3 value=0.) { gridSetConst<Vec3>(grid,value); } +PYTHON void setConstantInt (Grid<int >& grid, int value=0.) { gridSetConst<int>(grid,value); } + +template<class T> void Grid<T>::add(const Grid<T>& a) { + gridAdd<T,T>(*this, a); +} +template<class T> void Grid<T>::sub(const Grid<T>& a) { + gridSub<T,T>(*this, a); +} +KERNEL(idx) template<class T> void knGridSetAdded (Grid<T>& me, const Grid<T>& a, const Grid<T>& b) { + me[idx] = a[idx] + b[idx]; } +template<class T> void Grid<T>::setAdd(const Grid<T>& a, const Grid<T>& b) { + knGridSetAdded<T>(*this, a, b); +} +KERNEL(idx) template<class T> void knGridSetSubtracted (Grid<T>& me, const Grid<T>& a, const Grid<T>& b) { + me[idx] = a[idx] - b[idx]; } +template<class T> void Grid<T>::setSub(const Grid<T>& a, const Grid<T>& b) { + knGridSetSubtracted<T>(*this, a, b); +} +KERNEL(idx) template<class T> void knGridAddConstReal (Grid<T>& me, T val) { + me[idx] += val; } +template<class T> void Grid<T>::addConstReal(Real a) { + knGridAddConstReal<T>( *this, T(a) ); +} +KERNEL(idx) template<class T> void knGridMultConstReal (Grid<T>& me, Real val) { + me[idx] *= val; } +template<class T> void Grid<T>::multiplyConstReal(Real a) { + knGridMultConstReal<T>( *this, a ); +} + +template<class T> void Grid<T>::addScaledReal(const Grid<T>& b, const Real& factor) { + gridScaledAdd<T,T> (*this, b, factor); +} +template<class T> void Grid<T>::multiply(const Grid<T>& b) { + gridMult<T,T> (*this, b); +} + +KERNEL(idx) template<class T> void knGridClamp (Grid<T>& me, T min, T max) { me[idx] = clamp( me[idx], min, max); } +template<class T> void Grid<T>::clamp(Real min, Real max) { + knGridClamp<T> (*this, T(min), T(max) ); +} + +//! Grid a += b*factor (note, shouldnt be part of the grid class! can cause problems with python instantiation) +// (the template T class in argument list causes errors in fromPy etc. functions). +// Also the python integration doesnt support templated functions for now (only classes) +// So real and vec3 version are seperately declared here +/*PYTHON void scaledAddReal(Grid<Real>& a, const Grid<Real>& b, const Real& factor) { + gridScaledAdd<Real,Real> (a, b, factor); +} +PYTHON void scaledAddVec3(Grid<Vec3>& a, const Grid<Vec3>& b, const Vec3& factor) { + gridScaledAdd<Vec3,Vec3> (a, b, factor); +} */ +template<> Real Grid<Real>::getMaxValue() { + return CompMaxReal (*this); +} +template<> Real Grid<Real>::getMinValue() { + return CompMinReal (*this); +} +template<> Real Grid<Real>::getMaxAbsValue() { + Real amin = CompMinReal (*this); + Real amax = CompMaxReal (*this); + return max( fabs(amin), fabs(amax)); +} +template<> Real Grid<Vec3>::getMaxValue() { + return sqrt(CompMaxVec (*this)); +} +template<> Real Grid<Vec3>::getMinValue() { + return sqrt(CompMinVec (*this)); +} +template<> Real Grid<Vec3>::getMaxAbsValue() { + return sqrt(CompMaxVec (*this)); +} +template<> Real Grid<int>::getMaxValue() { + return (Real) CompMaxInt (*this); +} +template<> Real Grid<int>::getMinValue() { + return (Real) CompMinInt (*this); +} +template<> Real Grid<int>::getMaxAbsValue() { + int amin = CompMinInt (*this); + int amax = CompMaxInt (*this); + return max( fabs((Real)amin), fabs((Real)amax)); +} + +// compute maximal diference of two cells in the grid +// used for testing +PYTHON Real gridMaxDiff(Grid<Real>& g1, Grid<Real>& g2 ) +{ + double maxVal = 0.; + FOR_IJK(g1) { + maxVal = std::max(maxVal, (double)fabs( g1(i,j,k)-g2(i,j,k) )); + } + return maxVal; +} +PYTHON Real gridMaxDiffInt(Grid<int>& g1, Grid<int>& g2 ) +{ + double maxVal = 0.; + FOR_IJK(g1) { + maxVal = std::max(maxVal, (double)fabs( (double)g1(i,j,k)-g2(i,j,k) )); + } + return maxVal; +} +PYTHON Real gridMaxDiffVec3(Grid<Vec3>& g1, Grid<Vec3>& g2 ) +{ + double maxVal = 0.; + FOR_IJK(g1) { + // accumulate differences with double precision + // note - don't use norm here! should be as precise as possible... + double d = 0.; + for(int c=0; c<3; ++c) { + d += fabs( (double)g1(i,j,k)[c] - (double)g2(i,j,k)[c] ); + } + maxVal = std::max(maxVal, d ); + //maxVal = std::max(maxVal, (double)fabs( norm(g1(i,j,k)-g2(i,j,k)) )); + } + return maxVal; +} + +// simple helper functions to convert mac to vec3 , and levelset to real grids +// (are assumed to be the same for running the test cases - in general they're not!) +PYTHON void convertMacToVec3 (MACGrid &source, Grid<Vec3>& target) +{ + FOR_IJK(target) { + target(i,j,k) = source(i,j,k); + } +} + +PYTHON void convertLevelsetToReal (LevelsetGrid &source , Grid<Real> &target) +{ + FOR_IJK(target) { + target(i,j,k) = source(i,j,k); + } +} + + +template<class T> void Grid<T>::printGrid(int zSlice, bool printIndex) { + std::ostringstream out; + out << std::endl; + const int bnd = 1; + FOR_IJK_BND(*this,bnd) { + int idx = (*this).index(i,j,k); + if(zSlice>=0 && k==zSlice) { + out << " "; + if(printIndex) out << " "<<i<<","<<j<<","<<k <<":"; + out << (*this)[idx]; + if(i==(*this).getSizeX()-1 -bnd) out << std::endl; + } + } + out << endl; debMsg("Printing " << this->getName() << out.str().c_str() , 1); +} + +// helper functions for UV grid data (stored grid coordinates as Vec3 values, and uv weight in entry zero) + +// make uv weight accesible in python +PYTHON Real getUvWeight (Grid<Vec3> &uv) { return uv[0][0]; } + +// note - right now the UV grids have 0 values at the border after advection... could be fixed with an extrapolation step... + +// compute normalized modulo interval +static inline Real computeUvGridTime(Real t, Real resetTime) { + return fmod( (t / resetTime), (Real)1. ); +} +// create ramp function in 0..1 range with half frequency +static inline Real computeUvRamp(Real t) { + Real uvWeight = 2. * t; + if (uvWeight>1.) uvWeight=2.-uvWeight; + return uvWeight; +} + +KERNEL void knResetUvGrid (Grid<Vec3>& target) { target(i,j,k) = Vec3((Real)i,(Real)j,(Real)k); } + +PYTHON void resetUvGrid (Grid<Vec3> &target) +{ + knResetUvGrid reset(target); // note, llvm complains about anonymous declaration here... ? +} +PYTHON void updateUvWeight(Real resetTime, int index, int numUvs, Grid<Vec3> &uv , bool info=false) +{ + const Real t = uv.getParent()->getTime(); + Real timeOff = resetTime/(Real)numUvs; + + Real lastt = computeUvGridTime(t +(Real)index*timeOff - uv.getParent()->getDt(), resetTime); + Real currt = computeUvGridTime(t +(Real)index*timeOff , resetTime); + Real uvWeight = computeUvRamp(currt); + + // normalize the uvw weights , note: this is a bit wasteful... + Real uvWTotal = 0.; + for(int i=0; i<numUvs; ++i) { + uvWTotal += computeUvRamp( computeUvGridTime(t +(Real)i*timeOff , resetTime) ); + } + if(uvWTotal<=VECTOR_EPSILON) { uvWeight = uvWTotal = 1.; } + else uvWeight /= uvWTotal; + + // check for reset + if( currt < lastt ) + knResetUvGrid reset( uv ); + + // write new weight value to grid + uv[0] = Vec3( uvWeight, 0.,0.); + + // print info about uv weights? + if(info) debMsg("Uv grid "<<index<<"/"<<numUvs<< " t="<<currt<<" w="<<uvWeight<<", reset:"<<(int)(currt<lastt) , 1); +} + +PYTHON void setBoundaries(Grid<Real>& grid, Real value=0., int boundaryWidth=1) { + const int w = boundaryWidth; + FOR_IJK(grid) { + bool bnd = (i<=w || i>=grid.getSizeX()-1-w || j<=w || j>=grid.getSizeY()-1-w || (grid.is3D() && (k<=w || k>=grid.getSizeZ()-1-w))); + if (bnd) + grid(i,j,k) = value; + } +} + +//****************************************************************************** +// Specialization classes + +void FlagGrid::initDomain(int boundaryWidth) { + FOR_IDX(*this) + mData[idx] = TypeEmpty; + initBoundaries(boundaryWidth); +} + +void FlagGrid::initBoundaries(int boundaryWidth) { + const int w = boundaryWidth; + FOR_IJK(*this) { + bool bnd = (i<=w || i>=mSize.x-1-w || j<=w || j>=mSize.y-1-w || (is3D() && (k<=w || k>=mSize.z-1-w))); + if (bnd) + mData[index(i,j,k)] = TypeObstacle; + } +} + +void FlagGrid::updateFromLevelset(LevelsetGrid& levelset) { + FOR_IDX(*this) { + if (!isObstacle(idx)) { + const Real phi = levelset[idx]; + if (phi <= levelset.invalidTimeValue()) continue; + + mData[idx] &= ~(TypeEmpty | TypeFluid); // clear empty/fluid flags + mData[idx] |= (phi <= 0) ? TypeFluid : TypeEmpty; // set resepctive flag + } + } +} + +void FlagGrid::fillGrid(int type) { + FOR_IDX(*this) { + if ((mData[idx] & TypeObstacle)==0) + mData[idx] = (mData[idx] & ~(TypeEmpty | TypeFluid)) | type; + } +} + +// explicit instantiation +template class Grid<int>; +template class Grid<Real>; +template class Grid<Vec3>; + +//template void scaledAdd<Real,Real>(const Grid<Real>& a, const Grid<Real>& b, const Real& factor); + +#if ENABLE_GRID_TEST_DATATYPE==1 +// instantiate test datatype , not really required for simulations, mostly here for demonstration purposes +template class Grid<nbVector>; +#endif // ENABLE_GRID_TEST_DATATYPE + + +} //namespace diff --git a/source/blender/python/manta_full/source/grid.h b/source/blender/python/manta_full/source/grid.h new file mode 100644 index 00000000000..cc5580611b7 --- /dev/null +++ b/source/blender/python/manta_full/source/grid.h @@ -0,0 +1,473 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Grid representation + * + ******************************************************************************/ + +#ifndef _GRID_H +#define _GRID_H + +#include "manta.h" +#include "vectorbase.h" +#include "interpol.h" +#include "interpolHigh.h" +#include "kernel.h" + +namespace Manta { +class LevelsetGrid; + +//! Base class for all grids +PYTHON class GridBase : public PbClass { +public: + enum GridType { TypeNone = 0, TypeReal = 1, TypeInt = 2, TypeVec3 = 4, TypeMAC = 8, TypeLevelset = 16, TypeFlags = 32 }; + + PYTHON GridBase(FluidSolver* parent); + + //! Get the grids X dimension + inline int getSizeX() const { return mSize.x; } + //! Get the grids Y dimension + inline int getSizeY() const { return mSize.y; } + //! Get the grids Z dimension + inline int getSizeZ() const { return mSize.z; } + //! Get the grids dimensions + inline Vec3i getSize() const { return mSize; } + + //! Get Stride in X dimension + inline int getStrideX() const { return 1; } + //! Get Stride in Y dimension + inline int getStrideY() const { return mSize.x; } + //! Get Stride in Z dimension + inline int getStrideZ() const { return mStrideZ; } + + inline Real getDx() { return mDx; } + + //! Check if indices are within bounds, otherwise error (should only be called when debugging) + inline void checkIndex(int i, int j, int k) const; + //! Check if indices are within bounds, otherwise error (should only be called when debugging) + inline void checkIndex(int idx) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3i& p, int bnd) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3i& p) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3& p, int bnd = 0) const { return isInBounds(toVec3i(p), bnd); } + //! Check if linear index is in the range of the array + inline bool isInBounds(int idx) const; + + //! Get the type of grid + inline GridType getType() const { return mType; } + //! Check dimensionality + inline bool is2D() const { return !m3D; } + //! Check dimensionality + inline bool is3D() const { return m3D; } + + //! Get index into the data + inline int index(int i, int j, int k) const { DEBUG_ONLY(checkIndex(i,j,k)); return i + mSize.x * j + mStrideZ * k; } + //! Get index into the data + inline int index(const Vec3i& pos) const { DEBUG_ONLY(checkIndex(pos.x,pos.y,pos.z)); return pos.x + mSize.x * pos.y + mStrideZ * pos.z; } +protected: + + GridType mType; + Vec3i mSize; + Real mDx; + bool m3D; + // precomputed Z shift: to ensure 2D compatibility, always use this instead of sx*sy ! + int mStrideZ; +}; + +//! Grid class +PYTHON template<class T> +class Grid : public GridBase { +public: + //! init new grid, values are set to zero + PYTHON Grid(FluidSolver* parent, bool show = true); + //! create new & copy content from another grid + Grid(const Grid<T>& a); + //! return memory to solver + virtual ~Grid(); + + typedef T BASETYPE; + + PYTHON void save(std::string name); + PYTHON void load(std::string name); + + //! set all cells to zero + PYTHON void clear(); + + //! all kinds of access functions, use grid(), grid[] or grid.get() + //! access data + inline T get(int i,int j, int k) const { return mData[index(i,j,k)]; } + //! access data + inline T& get(int i,int j, int k) { return mData[index(i,j,k)]; } + //! access data + inline T get(int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T get(const Vec3i& pos) const { return mData[index(pos)]; } + //! access data + inline T& operator()(int i, int j, int k) { return mData[index(i, j, k)]; } + //! access data + inline T operator()(int i, int j, int k) const { return mData[index(i, j, k)]; } + //! access data + inline T& operator()(int idx) { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T operator()(int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T& operator()(const Vec3i& pos) { return mData[index(pos)]; } + //! access data + inline T operator()(const Vec3i& pos) const { return mData[index(pos)]; } + //! access data + inline T& operator[](int idx) { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline const T operator[](int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + + // interpolated access + inline T getInterpolated(const Vec3& pos) const { return interpol<T>(mData, mSize, mStrideZ, pos); } + inline void setInterpolated(const Vec3& pos, const T& val, Grid<Real>& sumBuffer) const { setInterpol<T>(mData, mSize, mStrideZ, pos, val, &sumBuffer[0]); } + // higher order interpolation + inline T getInterpolated(const Vec3& pos, int order) const { + switch(order) { + case 2: return interpolCubic<T>(mData, mSize, mStrideZ, pos); + default: + // default / fallback + assertMsg(false, "Unknown interpolation order "<<order); + case 1: + return interpol<T>(mData, mSize, mStrideZ, pos); + } + } + + // operators + template<class S> Grid<T>& operator+=(const Grid<S>& a); + template<class S> Grid<T>& operator+=(const S& a); + template<class S> Grid<T>& operator-=(const Grid<S>& a); + template<class S> Grid<T>& operator-=(const S& a); + template<class S> Grid<T>& operator*=(const Grid<S>& a); + template<class S> Grid<T>& operator*=(const S& a); + template<class S> Grid<T>& operator/=(const Grid<S>& a); + template<class S> Grid<T>& operator/=(const S& a); + Grid<T>& operator=(const Grid<T>& a); + Grid<T>& safeDivide(const Grid<T>& a); + + // python helper functions to work with grids in scene files + // note - unfortunately setConstant function has to be external! here only e.g. set to Real would work... + // see setConstant, setConstantVec3, setConstantInt in grid.cpp for details + + //! add/subtract other grid + PYTHON void add(const Grid<T>& a); + PYTHON void sub(const Grid<T>& a); + //! set content to added/subtracted values of other two grids + PYTHON void setAdd(const Grid<T>& a, const Grid<T>& b); + PYTHON void setSub(const Grid<T>& a, const Grid<T>& b); + //! add real constant to all grid cells + PYTHON void addConstReal(Real s); + //! multiply contents of grid + PYTHON void multiply( const Grid<T>& b); + //! multiply each cell by a constant scalar value + PYTHON void multiplyConstReal(Real s); + //! add scaled other grid to current one (note, only "Real" factor, "T" type not supported here!) + PYTHON void addScaledReal(const Grid<T>& b, const Real& factor); + //! copy content from other grid (use this one instead of operator= !) + PYTHON void copyFrom(const Grid<T>& a) { *this = a; } + //! clamp content to range (for vec3, clamps each component separately) + PYTHON void clamp(Real min, Real max); + + // common compound operators + //! get absolute max value in grid + PYTHON Real getMaxAbsValue(); + //! get max value in grid + PYTHON Real getMaxValue(); + //! get min value in grid + PYTHON Real getMinValue(); + //! Swap data with another grid (no actual data is moved) + void swap(Grid<T>& other); + + //! debugging helper, print grid from python + PYTHON void printGrid(int zSlice=-1, bool printIndex=false); + +protected: + T* mData; +}; + +// Python doesn't know about templates: explicit aliases needed +PYTHON alias Grid<int> IntGrid; +PYTHON alias Grid<Real> RealGrid; +PYTHON alias Grid<Vec3> VecGrid; + +//! Special function for staggered grids +PYTHON class MACGrid : public Grid<Vec3> { +public: + PYTHON MACGrid(FluidSolver* parent, bool show=true) : Grid<Vec3>(parent, show) { + mType = (GridType)(TypeMAC | TypeVec3); } + + // specialized functions for interpolating MAC information + inline Vec3 getCentered(int i, int j, int k) const; + inline Vec3 getCentered(const Vec3i& pos) const { return getCentered(pos.x, pos.y, pos.z); } + inline Vec3 getAtMACX(int i, int j, int k) const; + inline Vec3 getAtMACY(int i, int j, int k) const; + inline Vec3 getAtMACZ(int i, int j, int k) const; + template<int comp> inline Real getInterpolatedComponent(Vec3 pos) const { return interpolComponent<comp>(mData, mSize, mStrideZ, pos); } + inline Vec3 getInterpolated(const Vec3& pos) const { return interpolMAC(mData, mSize, mStrideZ, pos); } + inline void setInterpolated(const Vec3& pos, const Vec3& val, Vec3* tmp) { return setInterpolMAC(mData, mSize, mStrideZ, pos, val, tmp); } + +protected: +}; + +//! Special functions for FlagGrid +PYTHON class FlagGrid : public Grid<int> { +public: + PYTHON FlagGrid(FluidSolver* parent, int dim=3, bool show=true) : Grid<int>(parent, show) { + mType = (GridType)(TypeFlags | TypeInt); } + + //! types of cells, in/outflow can be combined, e.g., TypeFluid|TypeInflow + enum CellType { + TypeNone = 0, + TypeFluid = 1, + TypeObstacle = 2, + TypeEmpty = 4, + TypeInflow = 8, + TypeOutflow = 16, + TypeStick = 128, + TypeReserved = 256 + // 2^10 - 2^14 reserved for moving obstacles + }; + + //! access for particles + inline int getAt(const Vec3& pos) const { return mData[index((int)pos.x, (int)pos.y, (int)pos.z)]; } + + //! check for different flag types + inline bool isObstacle(int idx) const { return get(idx) & TypeObstacle; } + inline bool isObstacle(int i, int j, int k) const { return get(i,j,k) & TypeObstacle; } + inline bool isObstacle(const Vec3i& pos) const { return get(pos) & TypeObstacle; } + inline bool isObstacle(const Vec3& pos) const { return getAt(pos) & TypeObstacle; } + inline bool isFluid(int idx) const { return get(idx) & TypeFluid; } + inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & TypeFluid; } + inline bool isFluid(const Vec3i& pos) const { return get(pos) & TypeFluid; } + inline bool isFluid(const Vec3& pos) const { return getAt(pos) & TypeFluid; } + inline bool isInflow(int idx) const { return get(idx) & TypeInflow; } + inline bool isInflow(int i, int j, int k) const { return get(i,j,k) & TypeInflow; } + inline bool isInflow(const Vec3i& pos) const { return get(pos) & TypeInflow; } + inline bool isInflow(const Vec3& pos) const { return getAt(pos) & TypeInflow; } + inline bool isEmpty(int idx) const { return get(idx) & TypeEmpty; } + inline bool isEmpty(int i, int j, int k) const { return get(i,j,k) & TypeEmpty; } + inline bool isEmpty(const Vec3i& pos) const { return get(pos) & TypeEmpty; } + inline bool isEmpty(const Vec3& pos) const { return getAt(pos) & TypeEmpty; } + inline bool isStick(int idx) const { return get(idx) & TypeStick; } + inline bool isStick(int i, int j, int k) const { return get(i,j,k) & TypeStick; } + inline bool isStick(const Vec3i& pos) const { return get(pos) & TypeStick; } + inline bool isStick(const Vec3& pos) const { return getAt(pos) & TypeStick; } + + // Python callables + PYTHON void initDomain(int boundaryWidth=0); + PYTHON void initBoundaries(int boundaryWidth=0); + PYTHON void updateFromLevelset(LevelsetGrid& levelset); + PYTHON void fillGrid(int type=TypeFluid); +}; + + +//****************************************************************************** +// enable compilation of a more complicated test data type +// for grids... note - this also enables code parts in fileio.cpp! +// the code below is meant only as an example for a grid with a more complex data type +// and illustrates which functions need to be implemented; it's not needed +// to run any simulations in mantaflow! +#define ENABLE_GRID_TEST_DATATYPE 0 + +#if ENABLE_GRID_TEST_DATATYPE==1 + +typedef std::vector<int> nbVectorBaseType; +class nbVector : public nbVectorBaseType { + public: + inline nbVector() : nbVectorBaseType() {}; + inline ~nbVector() {}; + + inline const nbVector& operator+= ( const nbVector &v1 ) { + assertMsg(false,"Never call!"); return *this; + } + inline const nbVector& operator*= ( const nbVector &v1 ) { + assertMsg(false,"Never call!"); return *this; + } +}; + +template<> inline nbVector* FluidSolver::getGridPointer<nbVector>() { + return new nbVector[mGridSize.x * mGridSize.y * mGridSize.z]; +} +template<> inline void FluidSolver::freeGridPointer<nbVector>(nbVector* ptr) { + return delete[] ptr; +} + +inline nbVector operator+ ( const nbVector &v1, const nbVector &v2 ) { + assertMsg(false,"Never call!"); return nbVector(); +} +inline nbVector operator* ( const nbVector &v1, const nbVector &v2 ) { + assertMsg(false,"Never call!"); return nbVector(); +} +template<class S> +inline nbVector operator* ( const nbVector& v, S s ) { + assertMsg(false,"Never call!"); return nbVector(); +} +template<class S> +inline nbVector operator* ( S s, const nbVector& v ) { + assertMsg(false,"Never call!"); return nbVector(); +} + +template<> inline nbVector safeDivide<nbVector>(const nbVector &a, const nbVector& b) { + assertMsg(false,"Never call!"); return nbVector(); +} + +// make data type known to python +// python keyword changed here, because the preprocessor does not yet parse #ifdefs correctly +PYT HON alias Grid<nbVector> TestDataGrid; +#endif // ENABLE_GRID_TEST_DATATYPE + + +//! helper to compute grid conversion factor between local coordinates of two grids +inline Vec3 calcGridSizeFactor(Vec3i s1, Vec3i s2) { + return Vec3( Real(s1[0])/s2[0], Real(s1[1])/s2[1], Real(s1[2])/s2[2] ); +} + + +//****************************************************************************** +// Implementation of inline functions + +inline void GridBase::checkIndex(int i, int j, int k) const { + //if (i<0 || j<0 || i>=mSize.x || j>=mSize.y || (is3D() && (k<0|| k>= mSize.z))) { + if (i<0 || j<0 || i>=mSize.x || j>=mSize.y || k<0|| k>= mSize.z ) { + std::ostringstream s; + s << "Grid " << mName << " dim " << mSize << " : index " << i << "," << j << "," << k << " out of bound "; + errMsg(s.str()); + } +} + +inline void GridBase::checkIndex(int idx) const { + if (idx<0 || idx >= mSize.x * mSize.y * mSize.z) { + std::ostringstream s; + s << "Grid " << mName << " dim " << mSize << " : index " << idx << " out of bound "; + errMsg(s.str()); + } +} + +bool GridBase::isInBounds(const Vec3i& p) const { + return (p.x >= 0 && p.y >= 0 && p.z >= 0 && p.x < mSize.x && p.y < mSize.y && p.z < mSize.z); +} + +bool GridBase::isInBounds(const Vec3i& p, int bnd) const { + bool ret = (p.x >= bnd && p.y >= bnd && p.x < mSize.x-bnd && p.y < mSize.y-bnd); + if(this->is3D()) { + ret &= (p.z >= bnd && p.z < mSize.z-bnd); + } else { + ret &= (p.z == 0); + } + return ret; +} +//! Check if linear index is in the range of the array +bool GridBase::isInBounds(int idx) const { + if (idx<0 || idx >= mSize.x * mSize.y * mSize.z) { + return false; + } + return true; +} + +inline Vec3 MACGrid::getCentered(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i+1,j+1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3(0.5* (mData[idx].x + mData[idx+1].x), + 0.5* (mData[idx].y + mData[idx+mSize.x].y), + 0.); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ)); + v[2] = 0.5* (mData[idx].z + mData[idx+mStrideZ].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACX(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i-1,j+1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3( (mData[idx].x), + 0.25* (mData[idx].y + mData[idx-1].y + mData[idx+mSize.x].y + mData[idx+mSize.x-1].y), + 0.); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ-1)); + v[2] = 0.25* (mData[idx].z + mData[idx-1].z + mData[idx+mStrideZ].z + mData[idx+mStrideZ-1].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACY(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i+1,j-1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3(0.25* (mData[idx].x + mData[idx-mSize.x].x + mData[idx+1].x + mData[idx+1-mSize.x].x), + (mData[idx].y), 0. ); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ-mSize.x)); + v[2] = 0.25* (mData[idx].z + mData[idx-mSize.x].z + mData[idx+mStrideZ].z + mData[idx+mStrideZ-mSize.x].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACZ(int i, int j, int k) const { + const int idx = index(i,j,k); + DEBUG_ONLY(checkIndex(idx-mStrideZ)); + DEBUG_ONLY(checkIndex(idx+mSize.x-mStrideZ)); + Vec3 v = Vec3(0.25* (mData[idx].x + mData[idx-mStrideZ].x + mData[idx+1].x + mData[idx+1-mStrideZ].x), + 0.25* (mData[idx].y + mData[idx-mStrideZ].y + mData[idx+mSize.x].y + mData[idx+mSize.x-mStrideZ].y), + (mData[idx].z) ); + return v; +} + +KERNEL(idx) template<class T, class S> void gridAdd (Grid<T>& me, const Grid<S>& other) { me[idx] += other[idx]; } +KERNEL(idx) template<class T, class S> void gridSub (Grid<T>& me, const Grid<S>& other) { me[idx] -= other[idx]; } +KERNEL(idx) template<class T, class S> void gridMult (Grid<T>& me, const Grid<S>& other) { me[idx] *= other[idx]; } +KERNEL(idx) template<class T, class S> void gridDiv (Grid<T>& me, const Grid<S>& other) { me[idx] /= other[idx]; } +KERNEL(idx) template<class T, class S> void gridAddScalar (Grid<T>& me, const S& other) { me[idx] += other; } +KERNEL(idx) template<class T, class S> void gridMultScalar (Grid<T>& me, const S& other) { me[idx] *= other; } +KERNEL(idx) template<class T, class S> void gridScaledAdd (Grid<T>& me, const Grid<T>& other, const S& factor) { me[idx] += factor * other[idx]; } + +KERNEL(idx) template<class T> void gridAdd2 (Grid<T>& me, const Grid<T>& a, const Grid<T>& b) { me[idx] = a[idx] + b[idx]; } +KERNEL(idx) template<class T> void gridSafeDiv (Grid<T>& me, const Grid<T>& other) { me[idx] = safeDivide(me[idx], other[idx]); } +KERNEL(idx) template<class T> void gridSetConst(Grid<T>& grid, T value) { grid[idx] = value; } + +template<class T> template<class S> Grid<T>& Grid<T>::operator+= (const Grid<S>& a) { + gridAdd<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator+= (const S& a) { + gridAddScalar<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator-= (const Grid<S>& a) { + gridSub<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator-= (const S& a) { + gridAddScalar<T,S> (*this, -a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator*= (const Grid<S>& a) { + gridMult<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator*= (const S& a) { + gridMultScalar<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator/= (const Grid<S>& a) { + gridDiv<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator/= (const S& a) { + S rez((S)1.0 / a); + gridMultScalar<T,S> (*this, rez); + return *this; +} + + + +} //namespace +#endif diff --git a/source/blender/python/manta_full/source/gui/customctrl.cpp b/source/blender/python/manta_full/source/gui/customctrl.cpp new file mode 100644 index 00000000000..def8d2b03b7 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/customctrl.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GUI extension from python + * + ******************************************************************************/ + +#include "customctrl.h" +#include "qtmain.h" + +using namespace std; +namespace Manta { + +// ***************************************************************************** +// Slider class + +CustomControl::CustomControl() : PbClass(0) { +} + +CustomCheckbox::CustomCheckbox(string name, bool val) : mVal(val), mSName(name), mCheckbox(0) { +} + +void CustomCheckbox::init(QBoxLayout* layout) { + mCheckbox = new TextCheckbox(mSName, mVal); + QObject::connect(mCheckbox, SIGNAL(stateChanged(int)), mCheckbox, SLOT(update(int))); + mCheckbox->attach(layout); +} + +bool CustomCheckbox::get() { + if (!mCheckbox) throw Error("Slider is not attached yet!"); + return mCheckbox->get(); +} +void CustomCheckbox::set(bool v) { + if (!mCheckbox) throw Error("Slider is not attached yet!"); + mCheckbox->set(v); +} + +CustomSlider::CustomSlider(string name, float val, float min, float max) : + mMin(min), mMax(max), mVal(val), mSName(name), mSlider(0) +{ +} + +void CustomSlider::init(QBoxLayout* layout) { + mSlider = new TextSlider(mSName, mVal, mMin, mMax); + QObject::connect(mSlider, SIGNAL(valueChanged(int)), mSlider, SLOT(update(int))); + mSlider->attach(layout); +} + +float CustomSlider::get() { + if (!mSlider) throw Error("Slider is not attached yet!"); + return mSlider->get(); +} + +void CustomSlider::set(float v) { + if (!mSlider) throw Error("Slider is not attached yet!"); + mSlider->set(v); +} + +TextSlider::TextSlider(const string& name, float val, float vmin, float vmax) : + QSlider(Qt::Horizontal), mMin(vmin), mMax(vmax), mSName(name.c_str()) +{ + mLabel = new QLabel(); + mScale = 1000; + setMinimum(0); + setMaximum(999); + set(val); + update(0); + } + +void TextSlider::attach(QBoxLayout* layout) { + layout->addWidget(mLabel); + layout->addWidget(this); +} + +void TextSlider::update(int val) { + float v = get(); + QString num; + num.sprintf("%.2g", v); + mLabel->setText(mSName + ": " + num); +} + +float TextSlider::get() { + float va = mMin + (mMax-mMin) / mScale * (float)value(); + return clamp(va, mMin, mMax); +} + +void TextSlider::set(float v) { + float va = clamp(v, mMin, mMax); + va = (va - mMin) / (mMax-mMin) * mScale; + setValue((int)(va+0.5)); +} + +TextCheckbox::TextCheckbox(const string& name, bool val) : + QCheckBox(), mVal(val), mSName(name.c_str()) +{ + mLabel = new QLabel(); + set(val); + mLabel->setText(mSName); + } + +void TextCheckbox::attach(QBoxLayout* layout) { + QLayout* lay = new QHBoxLayout; + lay->setAlignment(Qt::AlignLeft); + lay->addWidget(this); + lay->addWidget(mLabel); + layout->addLayout(lay); +} + +void TextCheckbox::update(int val) { +} + +bool TextCheckbox::get() { + return isChecked(); +} + +void TextCheckbox::set(bool v) { + setChecked(v); +} + + + +// ************************************************************************************** +// GUI class + +void updateQtGui(bool full, int frame, const std::string& curPlugin); +extern MainThread* gMainThread; +extern GuiThread* gGuiThread; + +Gui::Gui() : + PbClass(NULL), mGuiPtr(gGuiThread), mMainPtr(gMainThread) { +} + +void Gui::setBackgroundMesh(Mesh* m) { + mGuiPtr->getWindow()->setBackground(m); +} +void Gui::show() { + mMainPtr->sendAndWait((int)MainWnd::EventGuiShow); +} +void Gui::update() { + updateQtGui(true,-1,""); +} +void Gui::pause() { + mMainPtr->sendAndWait((int)MainWnd::EventFullUpdate); + mGuiPtr->getWindow()->pause(); +} +void Gui::screenshot(string filename) { + QString s(filename.c_str()); + QMetaObject::invokeMethod(mGuiPtr->getWindow(), "screenshot", Q_ARG(QString, s)); +} + +PbClass* Gui::addControl(PbType t) { + _args.add("nocheck",true); + if (t.str() == "") + throw Error("Need to specify object type. Use e.g. gui.create(Slider, ...)"); + + PbClass* obj = PbClass::createPyObject(t.str(), "", _args, this); + if (!obj || !obj->canConvertTo("CustomControl")) + throw Error("gui.create() can only create CustomControl-based objects"); + + QMetaObject::invokeMethod(gGuiThread->getWindow(), "addControl", Q_ARG(void*, (void*)obj)); + + return obj; +} + + +} // namespace diff --git a/source/blender/python/manta_full/source/gui/customctrl.h b/source/blender/python/manta_full/source/gui/customctrl.h new file mode 100644 index 00000000000..c6fe94feefd --- /dev/null +++ b/source/blender/python/manta_full/source/gui/customctrl.h @@ -0,0 +1,129 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GUI extension from python + * + ******************************************************************************/ + +#ifndef _CUSTOMCTRL_H__ +#define _CUSTOMCTRL_H__ + +#include <QSlider> +#include <QLabel> +#include <QCheckBox> +#include <QBoxLayout> +#include "manta.h" + +namespace Manta { + +// fwd decl. +class Mesh; +class GuiThread; +class MainThread; + +//! Interface for python declared controls +PYTHON class CustomControl : public PbClass { +public: + PYTHON CustomControl(); + + virtual void init(QBoxLayout* layout) {}; + +protected: +}; + +//! Checkbox with attached text display +class TextCheckbox : public QCheckBox { + Q_OBJECT +public: + TextCheckbox(const std::string& name, bool val); + void attach(QBoxLayout* layout); + void set(bool v); + bool get(); + +public slots: + void update(int v); + +protected: + bool mVal; + QLabel* mLabel; + QString mSName; +}; + +//! Slider with attached text display +class TextSlider : public QSlider { + Q_OBJECT +public: + TextSlider(const std::string& name, float val, float min, float max); + void attach(QBoxLayout* layout); + void set(float v); + float get(); + +public slots: + void update(int v); + +protected: + float mMin, mMax, mScale; + QLabel* mLabel; + QString mSName; +}; + +//! Links a slider control +PYTHON(name=Slider) +class CustomSlider : public CustomControl { +public: + PYTHON CustomSlider(std::string text, float val, float min, float max); + virtual void init(QBoxLayout* layout); + + PYTHON float get(); + PYTHON void set(float v); + +protected: + float mMin, mMax, mVal; + std::string mSName; + TextSlider* mSlider; +}; + +//! Links a checkbox control +PYTHON(name=Checkbox) +class CustomCheckbox : public CustomControl { +public: + PYTHON CustomCheckbox(std::string text, bool val); + virtual void init(QBoxLayout* layout); + + PYTHON bool get(); + PYTHON void set(bool v); + +protected: + bool mVal; + std::string mSName; + TextCheckbox* mCheckbox; +}; + + +//! GUI adapter class to call from Python +PYTHON class Gui : public PbClass { +public: + PYTHON Gui(); + + PYTHON void setBackgroundMesh(Mesh* m); + PYTHON void show(); + PYTHON void update(); + PYTHON void pause(); + PYTHON PbClass* addControl(PbType t); + PYTHON void screenshot(std::string filename); + +protected: + GuiThread* mGuiPtr; + MainThread* mMainPtr; +}; + +} // namespace + +#endif + diff --git a/source/blender/python/manta_full/source/gui/glwidget.cpp b/source/blender/python/manta_full/source/gui/glwidget.cpp new file mode 100644 index 00000000000..5869a617e93 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/glwidget.cpp @@ -0,0 +1,292 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT OpenGL widget + * + ******************************************************************************/ + +#include "glwidget.h" +#ifdef __APPLE__ +# include <OpenGL/glu.h> +#else +# include <GL/glu.h> +#endif +#include <cmath> +#include "painter.h" + +namespace Manta { + +GLWidget::GLWidget(QWidget* p): QGLWidget(QGLFormat(QGL::SampleBuffers), p), mRotX(0), mRotY(0), mGridsize(0), mScreenshotNumber(0) +{ + mPlaneDim = 2; // Y plane + mPlane = -1; + mCamPos = Vec3(0, 0, -2); + for (int i=0; i<MoveDirNum; i++) + mMoveState[i] = false; + mMoveFast = false; + + setAutoBufferSwap(true); + setFocusPolicy(Qt::ClickFocus); + startTimer(10); +} + +GLWidget::~GLWidget() +{ + +} + +QSize GLWidget::minimumSizeHint() const +{ + return QSize(400, 300); +} + +QSize GLWidget::sizeHint() const +{ + return QSize(800, 600); +} + +void GLWidget::initializeGL() +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(1.0); +} + +void GLWidget::paintGL() +{ + if (mGridsize.max() == 0) return; + glDepthFunc(GL_ALWAYS); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + //glEnable(GL_POLYGON_OFFSET_FILL); + //glPolygonOffset(0,0); + + // camera transform + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(mCamPos.x, mCamPos.y, mCamPos.z); + glRotatef(mRotX, 1.,0.,0.); + glRotatef(mRotY, 0.,1.,0.); + Real dx = 1.0 / (Real) mGridsize.max(); + Vec3 sz = toVec3(mGridsize) * (-0.5f * dx); + + glTranslatef(sz.x, sz.y, sz.z); + emit paintSub(); +} + +void GLWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, (GLsizei) w, (GLsizei) h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + GLfloat fov = 45; + GLfloat zNear = 0.05f; + GLfloat zFar = 100.0f; + GLfloat aspect = float(w)/float(h); + GLfloat fH = tan( float(fov / 360.0f * M_PI) ) * zNear; + GLfloat fW = fH * aspect; + glFrustum( -fW, fW, -fH, fH, zNear, zFar ); + glMatrixMode(GL_MODELVIEW); + +} + +void GLWidget::mouseReleaseEvent(QMouseEvent* event) { + // only do tooltip if not moving + QPoint pos = event->pos(); + if ((mDownPos - pos).manhattanLength() == 0) { + // get GL transform matrices + int viewport[4]; + GLdouble modelMatrix[16], projMatrix[16]; + glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix); + glGetDoublev(GL_PROJECTION_MATRIX,projMatrix); + glGetIntegerv(GL_VIEWPORT,viewport); + + // obtain click line + GLdouble line[6], wx=pos.x(), wy=viewport[3]-pos.y(); + if (!gluUnProject(wx,wy,0,modelMatrix,projMatrix,viewport,&line[0],&line[1],&line[2])) return; + if (!gluUnProject(wx,wy,1.0,modelMatrix,projMatrix,viewport,&line[3],&line[4],&line[5])) return; + + // calculate intersection with plane + emit clickLine(event->globalPos(), line[0],line[1],line[2],line[3],line[4],line[5]); + } +} + +void GLWidget::mouseMoveEvent(QMouseEvent* e) +{ + const float speedRot = 0.2f, speedPan = 0.002f; + + QPoint diff = e->pos() - mAnchor; + if (e->buttons() & Qt::LeftButton) { + mRotX += diff.y() * speedRot; + mRotY += diff.x() * speedRot; + updateGL(); + } + if (e->buttons() & Qt::RightButton) { + mCamPos.x += diff.x() * speedPan; + mCamPos.y -= diff.y() * speedPan; + updateGL(); + } + + mAnchor = e->pos(); +} + +void GLWidget::mousePressEvent(QMouseEvent* e) +{ + mDownPos = mAnchor = e->pos(); +} + +void GLWidget::wheelEvent(QWheelEvent* e) +{ + const float speed = 0.002f; + mCamPos.z += speed*e->delta(); + updateGL(); +} + +void GLWidget::timerEvent(QTimerEvent* e) +{ + bool doRepaint = false; + + float speed = 0.005f; + if (mMoveFast) speed *= 5.; + + if (mMoveState[MoveLeft]) { mCamPos.x += speed; doRepaint = true; } + if (mMoveState[MoveRight]) { mCamPos.x -= speed; doRepaint = true; } + if (mMoveState[MoveUp]) { mCamPos.y -= speed; doRepaint = true; } + if (mMoveState[MoveDown]) { mCamPos.y += speed; doRepaint = true; } + if (mMoveState[MoveOut]) { mCamPos.z -= speed; doRepaint = true; } + if (mMoveState[MoveIn]) { mCamPos.z += speed; doRepaint = true; } + if (doRepaint) + updateGL(); +} + +void GLWidget::setViewport(const Vec3i& gridsize) { + if (mGridsize.x != gridsize.x || + mGridsize.y != gridsize.y || + mGridsize.z != gridsize.z) { + if (mPlane < 0) { + mPlane = gridsize[mPlaneDim] / 2; + } else { + Real fac = (Real)gridsize[mPlaneDim] / (Real)mGridsize[mPlaneDim]; + mPlane = (int)(fac * mPlane); + } + mGridsize = gridsize; + emit painterEvent(Painter::EventSetMax, mGridsize[mPlaneDim]); + emit painterEvent(Painter::EventSetPlane, mPlane); + } +} + +void GLWidget::keyPressEvent(QKeyEvent* e) +{ + if(!keyProcess(e->key(), e->modifiers(), true)) + QGLWidget::keyPressEvent(e); + else + updateGL(); +} + +void GLWidget::keyReleaseEvent(QKeyEvent* e) +{ + if(!keyProcess(e->key(), e->modifiers(), false)) + QGLWidget::keyReleaseEvent(e); + else + updateGL(); +} + +bool GLWidget::keyProcess(int key, int modifier, bool down) +{ + bool shift = (modifier & Qt::ShiftModifier); + bool alt = (modifier & Qt::AltModifier); + bool ctrl = (modifier & Qt::ControlModifier); + if (key == Qt::Key_A) { mMoveState[MoveLeft] = down; mMoveFast = shift; } + else if (key == Qt::Key_D) { mMoveState[MoveRight] = down; mMoveFast = shift; } + else if (key == Qt::Key_W) { mMoveState[MoveIn] = down; mMoveFast = shift; } + else if (key == Qt::Key_S) { mMoveState[MoveOut] = down; mMoveFast = shift; } + else if (key == Qt::Key_Q) { mMoveState[MoveUp] = down; mMoveFast = shift; } + else if (key == Qt::Key_E) { mMoveState[MoveDown] = down; mMoveFast = shift; } + else if (down) + { + // only press events + // note Key_P and Key_L used for play/step in mainwindow.cpp + if (key == Qt::Key_Z) { /* next "solver" info sometime? */ } + else if (key == Qt::Key_G) { emit painterEvent(Painter::EventToggleGridDisplay); } + // data grids, first int + else if (key == Qt::Key_X && shift) { /* int display mdoes, not yet used */ } + else if (key == Qt::Key_X) { emit painterEvent(Painter::EventNextInt); updatePlane(mPlane); } + // real + else if (key == Qt::Key_C && shift) { /* real display mdoes, not yet used */ } + else if (key == Qt::Key_C) { emit painterEvent(Painter::EventNextReal); updatePlane(mPlane); } + + // vec3 grids, scaling can be used with two key combinations (the second one is for international keyboards) + else if (key == Qt::Key_V && shift) { emit painterEvent(Painter::EventNextVelDisplayMode); } + else if (key == Qt::Key_V) { emit painterEvent(Painter::EventNextVec); updatePlane(mPlane); } + else if (key == Qt::Key_BraceLeft ) { emit painterEvent(Painter::EventScaleVecDown); } + else if (key == Qt::Key_BraceRight) { emit painterEvent(Painter::EventScaleVecUp); } + // grid scaling + else if (key == Qt::Key_BracketLeft && shift) { emit painterEvent(Painter::EventScaleVecDown); } + else if (key == Qt::Key_BracketRight && shift) { emit painterEvent(Painter::EventScaleVecUp); } + else if (key == Qt::Key_BracketLeft) { emit painterEvent(Painter::EventScaleRealDown); } + else if (key == Qt::Key_BracketRight) { emit painterEvent(Painter::EventScaleRealUp); } + + // particles + else if (key == Qt::Key_B && shift) { emit painterEvent(Painter::EventNextParticleDisplayMode); } + else if (key == Qt::Key_B && alt) { emit painterEvent(Painter::EventNextSystem); } + else if (key == Qt::Key_B) { emit painterEvent(Painter::EventToggleParticles); } + + else if((key == Qt::Key_ParenLeft) || // ugly, but for some reason parentheses dont work in some cases... fall back with dual assignment + ((key == Qt::Key_9) && shift) ) { emit painterEvent(Painter::EventScalePdataDown); } + else if((key == Qt::Key_ParenRight) || + ((key == Qt::Key_0) && shift ) ) { emit painterEvent(Painter::EventScalePdataUp); } + + // mesh display + else if (key == Qt::Key_M && shift) emit painterEvent(Painter::EventMeshMode); + else if((key == Qt::Key_Less) || + ((key == Qt::Key_Comma) && shift) ) { emit painterEvent(Painter::EventScaleMeshDown); } + else if((key == Qt::Key_Greater) || + ((key == Qt::Key_Period) && shift ) ) { emit painterEvent(Painter::EventScaleMeshUp); } + // special mesh display modes + else if (key == Qt::Key_M && alt) emit painterEvent(Painter::EventMeshColorMode); + else if (key == Qt::Key_M && ctrl) emit painterEvent(Painter::EventToggleBackgroundMesh); + else if (key == Qt::Key_M) emit painterEvent(Painter::EventNextMesh); + + // switch display planes + else if (key == Qt::Key_Asterisk) { + mPlaneDim = (mPlaneDim+1) % 3; + emit painterEvent(Painter::EventSetDim, mPlaneDim); + emit painterEvent(Painter::EventSetMax, mGridsize[mPlaneDim]); + } else if (key == Qt::Key_Plus || key == Qt::Key_Equal) { + updatePlane(mPlane + 1); + } else if (key == Qt::Key_Minus) { + updatePlane(mPlane - 1); + } + else if ( key == Qt::Key_K) { + QString filename = QString("scr_%1.png").arg(QString::number(mScreenshotNumber), 3, QChar('0')); + screenshot(filename); + mScreenshotNumber++; + } + + else return false; + } + else return false; + return true; +} + +void GLWidget::screenshot(QString file) { + grabFrameBuffer().save(file); +} + +void GLWidget::updatePlane(int plane) { + mPlane = clamp(plane, 0, mGridsize[mPlaneDim]); + emit painterEvent(Painter::EventSetPlane, mPlane); +} + + + +} diff --git a/source/blender/python/manta_full/source/gui/glwidget.h b/source/blender/python/manta_full/source/gui/glwidget.h new file mode 100644 index 00000000000..1601a7c7732 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/glwidget.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT OpenGL widget + * + ******************************************************************************/ + +#ifndef _GLWIDGET_H__ +#define _GLWIDGET_H__ + +#include <QGLWidget> +#include <QtOpenGL> +#include "vectorbase.h" + +namespace Manta { + +class GLWidget : public QGLWidget { +Q_OBJECT + +public: + GLWidget(QWidget *parent = NULL); + ~GLWidget(); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void wheelEvent(QWheelEvent *e); + void screenshot(QString file); + +public slots: + void setViewport(const Vec3i& gridsize); + void keyPressEvent(QKeyEvent* e); + void keyReleaseEvent(QKeyEvent* e); + +signals: + void paintSub(); + void clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2); + void painterEvent(int e, int param=0); + +protected: + bool keyProcess(int key, int mod, bool down); + void timerEvent(QTimerEvent* e); + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + void updatePlane(int plane); + + enum MoveDir { None = 0, MoveLeft, MoveRight, MoveUp, MoveDown, MoveIn, MoveOut, MoveDirNum }; + + bool mMoveState[MoveDirNum]; + bool mMoveFast; + QPoint mAnchor, mDownPos; + Vec3 mCamPos; + float mRotX, mRotY; + Vec3i mGridsize; + int mPlaneDim, mPlane; + + int mScreenshotNumber; +}; + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/gui/mainwindow.cpp b/source/blender/python/manta_full/source/gui/mainwindow.cpp new file mode 100644 index 00000000000..d176befbd8b --- /dev/null +++ b/source/blender/python/manta_full/source/gui/mainwindow.cpp @@ -0,0 +1,243 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT main window + * + ******************************************************************************/ + +#include "mainwindow.h" +#include "qtmain.h" + +#include <QLabel> +#include <QMenu> +#include <QMenuBar> +#include <QAction> +#include <QtOpenGL> +#include <sstream> +#include "meshpainter.h" +#include "particlepainter.h" + +using namespace std; + +namespace Manta { + +MainWnd::MainWnd() : QMainWindow(0), mPaused(true), mRequestPause(false), mRequestClose(false), mStep(0) +{ + // Frame info label + mInfo = new QLabel; + setFrame(0); + + // register GL widget + mGlWidget = new GLWidget(); + setCentralWidget(mGlWidget); + connect(mGlWidget, SIGNAL(clickLine(QPoint,float,float,float,float,float,float)), SLOT(clickLine(QPoint,float,float,float,float,float,float))); + + // register grid painters + mPainterLayout = new QVBoxLayout; + mPainterLayout->setAlignment(Qt::AlignTop); + mPainterLayout->addWidget(mInfo); + GridPainter<int>* intPainter = new GridPainter<int>(NULL, this); + mPainter.push_back(new GridPainter<Real>((FlagGrid**)intPainter->getGridPtr(), this)); + mPainter.push_back(new GridPainter<Vec3>(NULL, this)); + mPainter.push_back(intPainter); + mPainter.push_back(new ParticlePainter(intPainter, this)); + MeshPainter* ptr = new MeshPainter(this); + mPainter.push_back(ptr); + connect(this, SIGNAL(setBackgroundMesh(Mesh*)), ptr, SLOT(setBackgroundMesh(Mesh*))); + + for (int i=0; i<(int)mPainter.size(); i++) { + connect(mGlWidget, SIGNAL(paintSub()), mPainter[i], SLOT(paint())); + connect(mGlWidget, SIGNAL(painterEvent(int, int)), mPainter[i], SLOT(doEvent(int, int))); + connect(this, SIGNAL(painterEvent(int, int)), mPainter[i], SLOT(doEvent(int, int))); + connect(mPainter[i], SIGNAL(setViewport(const Vec3i&)), mGlWidget, SLOT(setViewport(const Vec3i&))); + mPainter[i]->attachWidget(mPainterLayout); + } + + // docking widget for painters + QDockWidget* painterDock = new QDockWidget("Info", this); + QWidget* painterProxy = new QWidget; + painterProxy->setLayout(mPainterLayout); + painterDock->setWidget(painterProxy); + painterDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + addDockWidget(Qt::RightDockWidgetArea, painterDock); + + // Top toolbar + QToolBar* toolbar = addToolBar("Control"); + toolbar->setAllowedAreas(Qt::TopToolBarArea); + toolbar->setMovable(false); + mAcPlay = toolbar->addAction(QIcon(":/play.png"),"Play"); + mAcPlay->setStatusTip("Continue simulation"); + connect(mAcPlay, SIGNAL(triggered()), SLOT(play())); + mAcPause = toolbar->addAction(QIcon(":/pause.png"),"Pause"); + mAcPause->setStatusTip("Pause simulation"); + connect(mAcPause, SIGNAL(triggered()), SLOT(pause())); + emit play(); + + // build menu + /*QAction* a = new QAction(this); + a->setText( "Quit" ); + connect(a, SIGNAL(triggered()), SLOT(close()) ); + menuBar()->addMenu( "File" )->addAction( a ); */ + + mGlWidget->setFocus(); + this->raise(); + this->activateWindow(); + + // uncomment to start paused + //emit pause(); +} + +void MainWnd::clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2) { + string msg; + for (int i=mPainter.size()-1; i>=0; i--) { + msg += mPainter[i]->clickLine(Vec3(p0,p1,p2),Vec3(q0,q1,q2)); + } + if (!msg.empty()) + QToolTip::showText(pos, QString(msg.c_str())); +} + +void MainWnd::addControl(void* ctrl) { + CustomControl* control = (CustomControl*) ctrl; + mCtrls.push_back(control); + control->init(mPainterLayout); +} + +void MainWnd::setFrame(int f) { + std::stringstream s; + s << "Simulation frame " << f; + mInfo->setText(s.str().c_str()); +} + +void MainWnd::setPauseStatus(bool v) +{ + mPaused = v; +} + +bool MainWnd::event(QEvent* e) { + if (e->type() == (QEvent::Type)EventGuiShow) { + if (!mRequestClose) { + this->show(); + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventFullUpdate) { + if (!mRequestClose) { + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventStepUpdate) { + if (!mRequestClose) { + if (mRequestPause) + emit painterEvent(Painter::UpdateFull); + else + emit painterEvent(Painter::UpdateStep); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventFinalUpdate) { + if (!mRequestClose) { + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + mRequestClose = true; + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventInstantKill) { + emit killMain(); + emit exitApp(); + return true; + } + + // update button states for pause events + if( (mRequestPause) && (!mAcPlay->isEnabled()) ) { + mAcPlay->setEnabled(true); + mAcPause->setEnabled(false); + } + if( (mRequestPause) && (!mAcPlay->isEnabled()) ) { + mAcPlay->setEnabled(true); + mAcPause->setEnabled(false); + } + + return QMainWindow::event(e); +} + +void MainWnd::keyPressEvent(QKeyEvent* e) { + if (e->key() == Qt::Key_Escape) { + mRequestClose = true; + emit killMain(); + this->close(); + } else if (e->key() == Qt::Key_Space) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else { + emit painterEvent(mPaused ? Painter::UpdateFull : Painter::UpdateRequest); + mGlWidget->updateGL(); + } + } else if (e->key() == Qt::Key_P) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else if (mRequestPause) + emit play(); + else + emit pause(); + } else if (e->key() == Qt::Key_L) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else if (mRequestPause) { + mRequestPause = false; + mStep = (e->modifiers() & Qt::ShiftModifier) ? 1 : 2; + } else + emit pause(); + } else { + mGlWidget->keyPressEvent(e); // let gl widget take care of keyboard shortcuts + //QMainWindow::keyPressEvent(e); + } +} +void MainWnd::keyReleaseEvent(QKeyEvent* e) +{ + mGlWidget->keyReleaseEvent(e); +} + +void MainWnd::pause() { + mRequestPause = true; + // dont call: mAcPlay/mAcPause ->setEnabled(true) here; wrong thread if called from python +} + +void MainWnd::play() { + mRequestPause = false; + mAcPlay->setEnabled(false); + mAcPause->setEnabled(true); +} + +void MainWnd::step() { + mStep = 2; + mRequestPause = false; +} + +MainWnd::~MainWnd() { +} + +void MainWnd::screenshot(QString file) { + mGlWidget->screenshot(file); +} + + +} diff --git a/source/blender/python/manta_full/source/gui/mainwindow.h b/source/blender/python/manta_full/source/gui/mainwindow.h new file mode 100644 index 00000000000..e3d11572685 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/mainwindow.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT main window + * + ******************************************************************************/ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include <QMainWindow> +#include "glwidget.h" +#include "customctrl.h" +#include "painter.h" +#include <vector> + +namespace Manta { +class Mesh; + +class MainWnd : public QMainWindow +{ +Q_OBJECT +public: + enum EventType { EventFullUpdate = QEvent::User, EventGuiShow, EventStepUpdate, EventFinalUpdate, EventInstantKill }; + + MainWnd(); + virtual ~MainWnd(); + bool event(QEvent* e); + void keyPressEvent(QKeyEvent* e); + void keyReleaseEvent(QKeyEvent* e); + inline bool pauseRequest() { return mRequestPause && !mRequestClose; } + inline bool closeRequest() { return mRequestClose; } + void setPauseStatus(bool v); + void stepReset(bool fullUpdate) { if (mStep == 1 || (mStep == 2 && fullUpdate)) {mRequestPause = true; mStep = 0;} } + void requestClose() { mRequestClose =true; } + void setFrame(int f); + void setBackground(Mesh *m) { emit setBackgroundMesh(m); } + +public slots: + void pause(); + void play(); + void step(); + void addControl(void* ctrl); + void screenshot(QString file); + void clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2); + +signals: + void painterEvent(int e, int param=0); + void wakeMain(); + void setBackgroundMesh(Mesh* bgr); + void killMain(); + void exitApp(); + +protected: + bool mPaused, mRequestPause, mRequestClose; + int mStep; + GLWidget* mGlWidget; + QAction* mAcPlay, *mAcPause; + std::vector<Painter*> mPainter; + std::vector<CustomControl*> mCtrls; + QLabel* mInfo; + QVBoxLayout* mPainterLayout; +}; + +} + +#endif diff --git a/source/blender/python/manta_full/source/gui/meshpainter.cpp b/source/blender/python/manta_full/source/gui/meshpainter.cpp new file mode 100644 index 00000000000..cba75ff2316 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/meshpainter.cpp @@ -0,0 +1,300 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting mesh objects + * + ******************************************************************************/ + +#include "meshpainter.h" +#include <QtOpenGL> +#include "mesh.h" +#include "vortexsheet.h" + +using namespace std; + +namespace Manta { + +MeshPainter::MeshPainter(QWidget* par) + : LockedObjPainter(par), mColorScale(1.0), mMode(ModeFlatShade), mVorticityMode(VModeNone), + mBackgroundMode(BModeNormal), mLocalMesh(0) , mBackground(0), mHide(false) +{ + mInfo = new QLabel(); +} + +MeshPainter::~MeshPainter() { + if (mLocalMesh) + delete mLocalMesh; +} + +void MeshPainter::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +void MeshPainter::update() { + Mesh* src = (Mesh*) mObject; + + // always reallocate + if (mLocalMesh) + delete mLocalMesh; + + mLocalMesh = src->clone(); + + updateText(); +} + +string MeshPainter::getID() { return "Mesh"; } + + +void MeshPainter::processKeyEvent(PainterEvent e, int param) +{ + if (e == EventNextMesh) + nextObject(); + else if (e == EventMeshMode) + mMode = (DisplayMode) ((mMode+1) % (int)Num_DisplayModes); + else if (e == EventScaleMeshUp) + mColorScale *= 2.0; + else if (e == EventScaleMeshDown) + mColorScale /= 2.0; + else if (e == EventMeshColorMode) + mVorticityMode = (VorticityMode) ((mVorticityMode+1)%(int)Num_VorticityModes); + else if (e == EventToggleBackgroundMesh) + mBackgroundMode = (BackgroundMode) ((mBackgroundMode+1)%(int)Num_BackgroundModes); + else return; + + updateText(); +} + +void MeshPainter::updateText() { + stringstream s; + + if ( mObject && !mHide && mLocalMesh ) { + s << "Mesh '" << mLocalMesh->getName() << "' [" << mLocalMesh->numTris() << " tris]" << endl; + if (mMode == ModeFlatShade) s << "DisplayMode: Flatshade" << endl; + if (mMode == ModeInvisible) s << "DisplayMode: Invisible" << endl; + if (mMode == ModeLines) s << "DisplayMode: Wireframe" << endl; + if (mMode == ModePoints) s << "DisplayMode: Pointset" << endl; + if (mMode == ModeTrans) s << "DisplayMode: Semi-Transparent" << endl; + if (mLocalMesh->getType() == Mesh::TypeVortexSheet) { + if (mVorticityMode==VModeFull) s << "Vorticity: full" << endl; + if (mVorticityMode==VModeSmoothed) s << "Vorticity: gauss smooth" << endl; + if (mVorticityMode==VModeDiff) s << "Vorticity: gauss diff" << endl; + if (mVorticityMode==VModeSmoke) s << "Smoke Density" << endl; + if (mVorticityMode==VModeTex) s << "Texture coordinates 0" << endl; + if (mVorticityMode!=VModeNone) s << " Scale " << 1.0/mColorScale << "" << endl; + } + } + mInfo->setText(s.str().c_str()); +} + +void MeshPainter::setBackgroundMesh(Mesh* bgr) { + mBackground = bgr; +} + +void MeshPainter::setupLights(bool specular) { + // control colors + float max = 1.0; + float dim = 0.5; + float dims = specular ? dim : 0; + float maxs = specular ? max : 0; + float amb = 0.1; + float shininess = 50.; + dim = 0.5; max = 0.75; amb = 0.25; + + float ambient0[] = {amb, amb, amb, max}; + glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0); + + float diffuse0[] = {max, dim, dim, 1.0}; + float specular0[] = {maxs, dims, dims, 1.0}; + float position0[] = { 5., 5., 5., 1.0f }; + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0); + glLightfv(GL_LIGHT0, GL_SPECULAR, specular0); + glLightfv(GL_LIGHT0, GL_POSITION, position0); + + float diffuse1[] = {dim, max, dim, 1.0}; + float specular1[] = {dims, maxs, dims, 1.0}; + float position1[] = { 5., -5., -5., 1.0f }; + glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1); + glLightfv(GL_LIGHT1, GL_SPECULAR, specular1); + glLightfv(GL_LIGHT1, GL_POSITION, position1); + + float diffuse2[] = {dim, dim, max, 1.0}; + float specular2[] = {dims, dims, maxs, 1.0}; + float position2[] = { 0.3, 2., -10., 1.0f }; + glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2); + glLightfv(GL_LIGHT2, GL_SPECULAR, specular2); + glLightfv(GL_LIGHT2, GL_POSITION, position2); + + float specReflection[] = { dims, dims, dims, 1.0f }; + if(shininess == 0.) specReflection[0] = specReflection[1] = specReflection[2] = 0.; + glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); + glMateriali(GL_FRONT, GL_SHININESS, shininess); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHT2); +} + +//static inline void glColor(const Vec3& color) { + //glColor3f(std::max(_0,std::min(_1,color.x)), std::max(_0,std::min(_1,color.y)), std::max(_0,std::min(_1,color.z))); +//} + +static inline void glVertex(const Vec3& v, Real dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} +static inline void glNormal(const Vec3& v) { + glNormal3f(v.x, v.y, v.z); +} + +void MeshPainter::paint() { + if (!mObject || mHide || !mLocalMesh) return; + + Real dx = mLocalMesh->getParent()->getDx(); + + bool triColor = (mMode == ModeFlatShade) && (mLocalMesh->getType() == Mesh::TypeVortexSheet) && (mVorticityMode!=VModeNone); + bool nodeColor = (mLocalMesh->getType() == Mesh::TypeVortexSheet) && (mVorticityMode==VModeTex); + + // setup OpenGL lighting and material + const float isoAlpha = 0.4; + setupLights(false); + glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ; + glEnable(GL_COLOR_MATERIAL); + glDepthFunc(GL_LESS); + //glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + // draw background + if (mBackground != NULL && mBackgroundMode != BModeInvisible) { + if (mBackgroundMode != BModeTrans) { + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glColor3f(0.3,0.3,0.5); + } else { + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glColor4f(0.6,0.6,0.8,0.5); + } + glEnable(GL_CULL_FACE); + glPolygonOffset(1.0, 0.5); + glBegin(GL_TRIANGLES); + + for(int tri=0; tri<mBackground->numTris(); tri++) { + Vec3 normal = mBackground->getFaceNormal(tri); + for (int c=0; c<3; c++) { + glNormal(normal); + glVertex(mBackground->getNode(tri,c), dx); + } + } + glEnd(); + glPolygonOffset(1., 1.); + glDisable(GL_CULL_FACE); + } + + setupLights(true); + if (mMode == ModeFlatShade) { + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } else { + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + } + if (triColor) + glDisable(GL_LIGHTING); + + // draw triangles + if (mMode == ModeFlatShade || mMode == ModeTrans) + { + glEnable(GL_CULL_FACE); + glPolygonOffset(1.0, 0.5); + glBegin(GL_TRIANGLES); + + const int numTris = (int)mLocalMesh->numTris(); + for(int tri=0; tri<numTris; tri++) { + if (!nodeColor && triColor) { + VortexSheetInfo& info = ((VortexSheetMesh*)mLocalMesh)->sheet(tri); + Vec3 v = info.vorticity; + if (mVorticityMode == VModeSmoothed) v = info.vorticitySmoothed; + if (mVorticityMode == VModeDiff) v -= info.vorticitySmoothed; + if (mVorticityMode == VModeSmoke) v = info.smokeAmount / 20.0f; + + Vec3 ca = v * 20.0 * mColorScale; + Vec3 color = Vec3(fabs(ca.x),fabs(ca.y),fabs(ca.z)); + glColor3f(color.x, color.y, color.z); + } else if (mLocalMesh->isTriangleFixed(tri)) + glColor3f(0,1,0); + else if (mLocalMesh->tris(tri).flags & Mesh::FfMarked) + glColor3f(1,0,0); + else + glColor4f(0.5,0.5,0.5, isoAlpha); + + for (int c=0; c<3; c++) { + if (nodeColor) { + Vec3 tc = ((VortexSheetMesh*)mLocalMesh)->tex1(mLocalMesh->tris(tri).c[c]); + //Vec3 tc2 = ((VortexSheetMesh*)mLocalMesh)->tex2(mLocalMesh->tris(tri).c[c]); + //Vec3 tc = gAlpha*tc1+(1-gAlpha)*tc2; + tc = mColorScale * (tc / toVec3(mLocalMesh->getParent()->getGridSize())); + tc = nmod(tc, Vec3(1,1,1)); + glColor3f(tc.x, tc.y ,tc.z); + } + glNormal(mLocalMesh->getFaceNormal(tri)); + glVertex(mLocalMesh->getNode(tri,c), dx); + } + } + glEnd(); + glPolygonOffset(1., 1.); + glDisable(GL_CULL_FACE); + } + + // Disable light setup + glDisable(GL_BLEND); + glDisable(GL_LIGHT0); + glDisable(GL_LIGHT1); + glDisable(GL_LIGHT2); + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_TEXTURE_2D); + + // draw mesh lines + if(mMode == ModeLines) { + glColor3d(1.0, 0.9, 0.9); + glLineWidth(1.0); + glBegin(GL_LINES); + const int numTris = (int)mLocalMesh->numTris(); + for(int tri=0; tri<numTris; tri++) + for (int j=5; j<5+6; j++) + glVertex( mLocalMesh->getNode(tri,(j/2)%3), dx); + glEnd(); + } + + // draw vertex points + if(mMode == ModePoints) { + static const Vec3 colorSpecial (0.3, 0.5, 0.2); + //static const Vec3 colortable[] = { Vec3(0.5), Vec3(1,0,0), Vec3(0,1,0), Vec3(0,0,1) }; + + glPointSize(2.0); + glBegin(GL_POINTS); + const int numNodes = (int)mLocalMesh->numNodes(); + for(int i=0; i<numNodes; i++) { + Vec3 color(0.5, 0.5, 0.5); + if (mLocalMesh->isNodeFixed(i)) + color = Vec3(0,1,0); + else if (mLocalMesh->nodes(i).flags & Mesh::NfMarked) + color = Vec3(1,0,0); + //int flags = mLocalMesh->flags(i); + + glColor3f(color.x, color.y, color.z); + glVertex(mLocalMesh->nodes(i).pos, dx); + } + glEnd(); + glPointSize(1.0); + } +} + + + +} // namespace diff --git a/source/blender/python/manta_full/source/gui/meshpainter.h b/source/blender/python/manta_full/source/gui/meshpainter.h new file mode 100644 index 00000000000..8a0166a2ed2 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/meshpainter.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting mesh objects + * + ******************************************************************************/ + +#ifndef _MESHPAINTER_H_ +#define _MESHPAINTER_H_ + +#include "painter.h" + +namespace Manta { +// fwd decl +class Mesh; + +//! Painter object for Meshes +class MeshPainter : public LockedObjPainter { + Q_OBJECT +public: + enum DisplayMode { ModeTrans=0, ModeLines, ModePoints, ModeFlatShade, ModeInvisible, Num_DisplayModes }; + enum BackgroundMode { BModeNormal=0, BModeTrans, BModeInvisible, Num_BackgroundModes }; + enum VorticityMode { VModeFull=0, VModeSmoothed, VModeDiff, VModeSmoke, VModeTex, VModeNone, Num_VorticityModes }; + + MeshPainter(QWidget* par = 0); + ~MeshPainter(); + + void paint(); + void attachWidget(QLayout* layout); + +public slots: + void setBackgroundMesh(Mesh* bgr); + +protected: + std::string getID(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + void processSpecificKeyEvent(PainterEvent e, int param); + void setupLights(bool specular); + + Real mColorScale; + DisplayMode mMode; + VorticityMode mVorticityMode; + BackgroundMode mBackgroundMode; + Mesh* mLocalMesh, *mBackground; + QLabel* mInfo; + bool mHide; +}; + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/gui/painter.cpp b/source/blender/python/manta_full/source/gui/painter.cpp new file mode 100644 index 00000000000..7437c8d6fed --- /dev/null +++ b/source/blender/python/manta_full/source/gui/painter.cpp @@ -0,0 +1,539 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for objects painting into the GL widget + * + ******************************************************************************/ + +#include "painter.h" +#include <QtOpenGL> +#include <sstream> +#include <iomanip> + +using namespace std; + +namespace Manta { + +//****************************************************************************** +// Base class + +void LockedObjPainter::doEvent(int e, int param) { + // try to obtain valid handle + if (!mObject) + nextObject(); + + // filter update events + if (e == UpdateFull) { + // always update + if (mObject) { + mObject->lock(); + update(); + mObject->unlock(); + mRequestUpdate = false; + } + } else if (e == UpdateRequest) { + // update if resource is available, otherwise wait until next step + mRequestUpdate = true; + if (mObject) { + if (mObject->tryLock()) { + update(); + mRequestUpdate = false; + mObject->unlock(); + } + } + } else if (e == UpdateStep) { + // update if requested only + if (mRequestUpdate) { + if (mObject) { + mObject->lock(); + update(); + mObject->unlock(); + mRequestUpdate = false; + } + } + } else { + // pass on all other events + processKeyEvent((PainterEvent)e, param); + } +} + +void LockedObjPainter::nextObject() { + if (PbClass::getNumInstances() == 0) return; + + int oldIndex = mObjIndex; + for(;;) { + mObjIndex = (mObjIndex + 1) % PbClass::getNumInstances(); + if (oldIndex == mObjIndex) break; + + PbClass* obj = PbClass::getInstance(mObjIndex); + if (obj->canConvertTo(getID()) && !obj->isHidden()) { + mObject = obj; + doEvent(UpdateRequest); + return; + } + if (oldIndex < 0) oldIndex = 0; // prevent endless loop on first run + } +} + +//****************************************************************************** +// Grid painter + +template<class T> +GridPainter<T>::GridPainter(FlagGrid** flags, QWidget* par) + : LockedObjPainter(par), mMaxVal(0), mDim(0), mPlane(0), mMax(0), mLocalGrid(NULL), + mFlags(flags), mInfo(NULL), mHide(false), mHideLocal(false), mVelMode(VelDispCentered), mValScale() +{ + mDim = 2; // Z plane + mPlane = 0; + mInfo = new QLabel(); + +} + +template<class T> +GridPainter<T>::~GridPainter() { + if (mLocalGrid) + delete mLocalGrid; +} + +template<class T> +void GridPainter<T>::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +template<class T> +void GridPainter<T>::update() { + Grid<T>* src = (Grid<T>*) mObject; + + if (!mLocalGrid) { + mLocalGrid = new Grid<T>(src->getParent()); + // int grid is base for resolution + if (src->getType() & GridBase::TypeInt) + emit setViewport(src->getSize()); + } + // reallocate if dimensions changed + if (mLocalGrid->getSize() != src->getSize()) { + delete mLocalGrid; + mLocalGrid = new Grid<T>(src->getParent()); + // int grid is base for resolution + if (src->getType() & GridBase::TypeInt) + emit setViewport(src->getSize()); + } + + *mLocalGrid = *src; // copy grid data and type marker + mLocalGrid->setName(src->getName()); + mLocalGrid->setParent(src->getParent()); + mMaxVal = mLocalGrid->getMaxAbsValue(); + + mPlane = clamp(mPlane, 0, mLocalGrid->getSize()[mDim]-1); + + updateText(); +} + +template<> string GridPainter<int>::getID() { return "Grid<int>"; } +template<> string GridPainter<Vec3>::getID() { return "Grid<Vec3>"; } +template<> string GridPainter<Real>::getID() { return "Grid<Real>"; } + +template<class T> +void GridPainter<T>::processKeyEvent(PainterEvent e, int param) +{ + if (e == EventSetDim) { + mDim = param; + if (mLocalGrid->is2D()) mDim = 2; + } else if (e == EventSetMax) { + mMax = param; + } else if (e == EventSetPlane) { + mPlane = param; + if (mObject) { + if (mMax>0) + mPlane = mPlane * mLocalGrid->getSize()[mDim] / mMax; + mPlane = clamp(mPlane, 0, mLocalGrid->getSize()[mDim]-1); + } + } else if (e == EventToggleGridDisplay) + mHide = !mHide; + else + processSpecificKeyEvent(e, param); + + updateText(); +} + +// get scale value for current grid from map, or create new +template<class T> +Real GridPainter<T>::getScale() { + if (!mObject) return 0; + + if (mValScale.find(mObject) == mValScale.end()) { + // init new scale value + Real s = 1.0; + if (mLocalGrid->getType() & GridBase::TypeVec3) + s = 0.4; + else if (mLocalGrid->getType() & GridBase::TypeLevelset) + s = 1.0; + mValScale[mObject] = s; + } + return mValScale[mObject]; + +} + +//****************************************************************************** +// Grid painter class specializations + +template<> +void GridPainter<int>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextInt) + nextObject(); +} + +template<> +void GridPainter<Real>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextReal) + nextObject(); + else if (e == EventScaleRealDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScaleRealUp && mObject) + mValScale[mObject] = getScale() * 2.0; +} + +template<> +void GridPainter<Vec3>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextVec) + nextObject(); + else if (e == EventScaleVecDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScaleVecUp && mObject) + mValScale[mObject] = getScale() * 2.0; + else if (e == EventNextVelDisplayMode) { + mVelMode = (mVelMode+1)%NumVelDispModes; + mHideLocal = (mVelMode==VelDispOff); + + } +} + +template<> void GridPainter<int>::updateText() { + stringstream s; + if (mObject && (!mHide)) { + s << "Int Grid '" << mLocalGrid->getName() << "'" << endl; + } + mInfo->setText(s.str().c_str()); +} + +template<> void GridPainter<Real>::updateText() { + stringstream s; + + s << "Display Plane " << mPlane << " [" << (char)('X' + mDim) << "]" << endl << endl; + if (mObject) { + s << "Solver '" << mLocalGrid->getParent()->getName() << "'" << endl; + s << "Grid resolution [" << mLocalGrid->getSizeX() << ", " << mLocalGrid->getSizeY() << ", " << mLocalGrid->getSizeZ() << "]" << endl; + s << endl; + } + + if (mObject && !mHide) { + s << "Real Grid '" << mLocalGrid->getName() << "'" << endl; + s << "-> Max " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + mInfo->setText(s.str().c_str()); +} + +template<> void GridPainter<Vec3>::updateText() { + stringstream s; + if (mObject && !mHide && !mHideLocal) { + s << "Vec Grid '" << mLocalGrid->getName() << "'" << endl; + s << "-> Max norm " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + mInfo->setText(s.str().c_str()); +} + +// compute line intersection with the display plane +Vec3i getQuad(const Vec3& l0, const Vec3& l1, int dim, int plane, Real dx) { + Vec3 n(0.); n[dim] = 1; + Vec3 p0 = n*(plane+0.5); + Vec3 e = (l1-l0)/dx; + Vec3 e0 = l0/dx; + Real dotP = dot(p0-e0,n); + Real dotE = dot(e,n); + if (dotE == 0) + return Vec3i(-1,-1,-1); + Vec3 s = e0 + (dotP/dotE)*e; + return toVec3i(s); +} + +template<> string GridPainter<int>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << "Grid [ " << s.x << ", " << s.y << ", " << s.z << " ]" << endl << mLocalGrid->getName() << ": " << mLocalGrid->get(s) << endl; + return m.str(); +} + +template<> string GridPainter<Real>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << mLocalGrid->getName() << ": " << setprecision(2) << mLocalGrid->get(s) << endl; + return m.str(); +} + +template<> string GridPainter<Vec3>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << mLocalGrid->getName() << ": [ " << setprecision(2) << mLocalGrid->get(s).x << ", " << + mLocalGrid->get(s).y << ", " << mLocalGrid->get(s).z << " ]" << endl; + return m.str(); +} + + +//****************************************************************************** +// Actual painting functions + +// GL helper functions + +// Macro to iterate through one plane +#define FOR_P_SLICE(__g,__dim,__plane) \ + for(Vec3i __g0(__fRange(Vec3i(0,0,0),__dim,__plane)), __g1(__fRange((__g)->getSize(),__dim,__plane+1)), p(__g0); p.z<__g1.z; p.z++) \ + for(p.y=__g0.y; p.y < __g1.y; p.y++) \ + for(p.x=__g0.x; p.x < __g1.x; p.x++) +inline Vec3i __fRange(Vec3i size, int dim, int plane) { Vec3i p(size); p[dim]=plane; return p; } + +// coordinate system : +// cell center(i,j,k) -> (i+0.5,j+0.5,k+0.5) / N +// + +void getCellCoordinates(const Vec3i& pos, Vec3 box[4], int dim) { + int dim2=(dim+1)%3; + Vec3 p0(pos.x, pos.y, pos.z); + Vec3 p1(pos.x+1, pos.y+1, pos.z+1); + p1[dim] = p0[dim] = pos[dim] + 0.5; + box[0] = p0; + box[3] = p0; box[3][dim2] = p1[dim2]; + box[1] = p1; box[1][dim2] = p0[dim2]; + box[2] = p1; +} +static inline void glVertex(const Vec3& v, const float dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} +void glBox(const Vec3& p0, const Vec3& p1, const float dx) { + const int box[24] = {0,1,0,2,0,4,7,6,7,5,7,3,1,3,1,5,2,3,2,6,4,5,4,6}; + for (int i=0;i<24;i++) { + const int b = box[i]; + glVertex(Vec3( (b&1) ? p1.x : p0.x, (b&2) ? p1.y : p0.y, (b&4) ? p1.z : p0.z), dx); + } +} + +// Paint gridlines +template<> void GridPainter<int>::paint() { + if (!mObject || mHide || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim]) + return; + float dx = mLocalGrid->getDx(); + Vec3 box[4]; + glColor3f(0.5,0,0); + + bool rbox = true; + bool skipFluid = mLocalGrid->getSize().max() > 40; + bool drawLines = mLocalGrid->getSize().max() <= 80; + if (drawLines) { + //glDepthFunc(GL_LESS); + glBegin(GL_LINES); + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + + int flag = 0; + flag = mLocalGrid->get(p); + + if (flag & FlagGrid::TypeObstacle) { + glColor3f(0.2,0.2,0.2); + } else if (flag & FlagGrid::TypeOutflow) { + glColor3f(0.9,0.2,0); + } else if (flag & FlagGrid::TypeEmpty) { + glColor3f(0.25,0,0); + } else if (flag & FlagGrid::TypeFluid) { + if(skipFluid) continue; + glColor3f(0,0,0.75); + } else { + glColor3f(0.5,0,0); // unknown + } + + getCellCoordinates(p, box, mDim); + for (int n=1;n<=8;n++) + glVertex(box[(n/2)%4], dx); + } + glEnd(); + //glDepthFunc(GL_ALWAYS); + } + + if (rbox) { + Vec3 p0(0.0), p1(toVec3(mLocalGrid->getSize())),p(p0); + glDepthFunc(GL_LESS); + glBegin(GL_LINES); + glBox(p0,p1,dx); + glEnd(); + glDepthFunc(GL_ALWAYS); + } +} + +// Paint box colors +template<> void GridPainter<Real>::paint() { + if (!mObject || mHide || mHideLocal || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim] || !mFlags || !(*mFlags)) + return; + + float dx = mLocalGrid->getDx(); + Vec3 box[4]; + glBegin(GL_QUADS); + Real scale = getScale(); + bool isLevelset = mLocalGrid->getType() & GridBase::TypeLevelset; + //glPolygonOffset(1.0,1.0); + //glDepthFunc(GL_LESS); + + const bool useOldDrawStyle = false; + if(useOldDrawStyle) { + // original mantaflow drawing style + FlagGrid *flags = *mFlags; + if (flags->getSize() != mLocalGrid->getSize()) flags = 0; + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + int flag = FlagGrid::TypeFluid; + if (flags && (mLocalGrid->getType() & GridBase::TypeLevelset) == 0) flag = flags->get(p); + if (flag & FlagGrid::TypeObstacle) + glColor3f(0.15,0.15,0.15); + else if (flag & FlagGrid::TypeOutflow) + glColor3f(0.3,0.0,0.0); + else if (flag & FlagGrid::TypeEmpty) + glColor3f(0.,0.2,0.); + else { + Real v = mLocalGrid->get(p) * scale; + + if (isLevelset) { + v = max(min(v*0.2, 1.0),-1.0); + if (v>=0) + glColor3f(v,0,0.5); + else + glColor3f(0.5, 1.0+v, 0.); + } else { + if (v>0) + glColor3f(v,0,0); + else + glColor3f(0,0,-v); + } + } + + if ((flag & FlagGrid::TypeEmpty) == 0) { + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + } + + } else { + // "new" drawing style + // ignore flags, its a bit dangerous to skip outside info + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) + { + Real v = mLocalGrid->get(p) * scale; + if (isLevelset) { + v = max(min(v*0.2, 1.0),-1.0); + if (v>=0) + glColor3f(v,0,0.5); + else + glColor3f(0.5, 1.0+v, 0.); + } else { + if (v>0) + glColor3f(v,v,v); + else + glColor3f(-v,0,0); + } + + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + } + + glEnd(); + //glDepthFunc(GL_ALWAYS); + //glPolygonOffset(0,0); +} + +// Paint velocity vectors +template<> void GridPainter<Vec3>::paint() { + if (!mObject || mHide || mHideLocal || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim]) + return; + + float dx = mLocalGrid->getDx(); + bool mac = mLocalGrid->getType() & GridBase::TypeMAC; + const Real scale = getScale(); + + if( (mVelMode==VelDispCentered) || (mVelMode==VelDispStaggered) ) { + + // regular velocity drawing mode + glBegin(GL_LINES); + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + Vec3 vel = mLocalGrid->get(p) * scale; + Vec3 pos (p.x+0.5, p.y+0.5, p.z+0.5); + if (mVelMode==VelDispCentered) { + if (mac) { + if (p.x < mLocalGrid->getSizeX()-1) + vel.x = 0.5 * (vel.x + scale * mLocalGrid->get(p.x+1,p.y,p.z).x); + if (p.y < mLocalGrid->getSizeY()-1) + vel.y = 0.5 * (vel.y + scale * mLocalGrid->get(p.x,p.y+1,p.z).y); + if (p.z < mLocalGrid->getSizeZ()-1) + vel.z = 0.5 * (vel.z + scale * mLocalGrid->get(p.x,p.y,p.z+1).z); + } + glColor3f(0,1,0); + glVertex(pos, dx); + glColor3f(1,1,0); + glVertex(pos+vel*1.2, dx); + } else if (mVelMode==VelDispStaggered) { + for (int d=0; d<3; d++) { + if (fabs(vel[d]) < 1e-2) continue; + Vec3 p1(pos); + if (mac) + p1[d] -= 0.5f; + Vec3 color(0.0); + color[d] = 1; + glColor3f(color.x, color.y, color.z); + glVertex(p1, dx); + glColor3f(1,1,0); + p1[d] += vel[d]; + glVertex(p1, dx); + } + } + } + glEnd(); + + } else if (mVelMode==VelDispUv) { + // draw as uv coordinates , note - this will completely hide the real grid display! + Vec3 box[4]; + glBegin(GL_QUADS); + FOR_P_SLICE(mLocalGrid, mDim, mPlane) + { + Vec3 v = mLocalGrid->get(p) * scale; + for(int c=0; c<3; ++c) { + if(v[c]<0.) v[c] *= -1.; + v[c] = fmod( (Real)v[c], (Real)1.); + } + //v *= mLocalGrid->get(0)[0]; // debug, show uv grid weight as brightness of values + glColor3f(v[0],v[1],v[2]); + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + glEnd(); + } +} + + +// explicit instantiation +template class GridPainter<int>; +template class GridPainter<Real>; +template class GridPainter<Vec3>; + +} // namespace diff --git a/source/blender/python/manta_full/source/gui/painter.h b/source/blender/python/manta_full/source/gui/painter.h new file mode 100644 index 00000000000..8d5f9db229b --- /dev/null +++ b/source/blender/python/manta_full/source/gui/painter.h @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for objects painting into the GL widget + * + ******************************************************************************/ + +#ifndef _PAINTER_H_ +#define _PAINTER_H_ + +#include <QWidget> +#include <QLabel> +#include <map> +#include "grid.h" + +namespace Manta { + +// forward decl. +class PbClass; + +//! Base class for all painter +/*! Derived classes have to implement paint, doEvent */ +class Painter : public QObject { + Q_OBJECT +public: + enum PainterEvent { + EventNone = 0, UpdateRequest, UpdateFull, UpdateStep, + EventScaleVecUp, EventScaleVecDown, EventScaleRealUp, EventScaleRealDown, EventChangePlane, + EventSetPlane, EventSetDim, EventNextInt, EventNextReal, EventNextVec, EventNextVelDisplayMode, + EventNextMesh, EventMeshMode, EventToggleGridDisplay, EventScaleMeshUp, EventScaleMeshDown, EventMeshColorMode, + EventNextSystem, EventToggleParticles, EventNextParticleDisplayMode, EventToggleBackgroundMesh, EventSetMax, + EventScalePdataDown, EventScalePdataUp }; + + enum VelDisplayModes { VelDispOff=0, VelDispCentered, VelDispStaggered, VelDispUv, NumVelDispModes }; + + Painter(QWidget* par = 0) : QObject(par) {} + virtual ~Painter() {} + + virtual std::string clickLine(const Vec3& p0, const Vec3& p1) { return ""; } + virtual void attachWidget(QLayout* layout) {} +signals: + void setViewport(const Vec3i& gridsize); + +public slots: + virtual void paint() = 0; + virtual void doEvent(int e, int param=0) = 0; +}; + +//! Base clas for all painters that require access to a locked PbClass +/*! Derived classes have to implement paint, update, getID, processKeyEvent. doEvent is handled in this class */ +class LockedObjPainter : public Painter { + Q_OBJECT +public: + LockedObjPainter(QWidget* par = 0) : Painter(par), mRequestUpdate(false), mObject(NULL), mObjIndex(-1) {} + + void doEvent(int e, int param=0); // don't overload, use processKeyEvent and update instead + +protected: + void nextObject(); + virtual std::string getID() = 0; + virtual void update() = 0; + virtual void processKeyEvent(PainterEvent e, int param) = 0; + + bool mRequestUpdate; + PbClass* mObject; + int mObjIndex; +}; + +//! Painter object for int,Real,Vec3 grids +template<class T> +class GridPainter : public LockedObjPainter { +public: + GridPainter(FlagGrid** flags = NULL, QWidget* par = 0); + ~GridPainter(); + + void paint(); + void attachWidget(QLayout* layout); + Grid<T>** getGridPtr() { return &mLocalGrid; } + int getPlane() { return mPlane; } + int getDim() { return mDim; } + int getMax() { return mMax; } + virtual std::string clickLine(const Vec3& p0, const Vec3& p1); + +protected: + std::string getID(); + Real getScale(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + void processSpecificKeyEvent(PainterEvent e, int param); + //void paintGridLines(bool lines, bool box); + + Real mMaxVal; + int mDim, mPlane, mMax; + Grid<T>* mLocalGrid; + FlagGrid** mFlags; + QLabel* mInfo; + bool mHide, mHideLocal; + int mVelMode; + std::map<PbClass*, Real> mValScale; +}; + +} + +#endif diff --git a/source/blender/python/manta_full/source/gui/particlepainter.cpp b/source/blender/python/manta_full/source/gui/particlepainter.cpp new file mode 100644 index 00000000000..f9761255f51 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/particlepainter.cpp @@ -0,0 +1,456 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting particle systems + * + ******************************************************************************/ + +#include <ctime> +#include "particlepainter.h" +#include <sstream> +#include <iomanip> +#include <QtOpenGL> +#include "vortexpart.h" +//#include "vortexfilament.h" +#include "turbulencepart.h" + +using namespace std; + +namespace Manta { + +ParticlePainter::ParticlePainter(GridPainter<int>* gridRef, QWidget* par) + : LockedObjPainter(par), mGridRef(gridRef), mLocal(0), mMode(PaintVel), mDisplayMode(0), + mLastPdata(-1), mHavePdata(false), mMaxVal(0.) +{ + mInfo = new QLabel(); +} + +ParticlePainter::~ParticlePainter() { + if (mLocal) + delete mLocal; +} + +void ParticlePainter::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +void ParticlePainter::update() { + ParticleBase* src = (ParticleBase*) mObject; + + // always reallocate + if (mLocal) + delete mLocal; + + mLocal = src->clone(); + + updateText(); +} + +string ParticlePainter::getID() { return "ParticleBase"; } + +Real ParticlePainter::getScale() { + if (!mObject) return 0; + + if (mValScale.find(mObject) == mValScale.end()) { + Real s = 1.0; + //if (mLocalGrid->getType() & GridBase::TypeVec3) s = 0.4; + mValScale[mObject] = s; + } + return mValScale[mObject]; + +} + +void ParticlePainter::processKeyEvent(PainterEvent e, int param) { + if (e == EventNextSystem) + nextObject(); + else if (e == EventScalePdataDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScalePdataUp && mObject) + mValScale[mObject] = getScale() * 2.0; + else if (e == EventToggleParticles) { + mMode++; // apply modulo later depending on particle system + //if(mMode>PaintVel) mMode=PaintOff; + } + else if (e == EventNextParticleDisplayMode) { + mDisplayMode++; + } + else return; + + updateText(); +} + +void ParticlePainter::updateText() { + ostringstream s; + + if (mObject && !(mMode==PaintOff) ) { + s << mLocal->infoString() << endl; + s << mPdataInfo; + if(mHavePdata) { + s << "-> Max " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + } + mInfo->setText( s.str().c_str() ); +} + + +static inline void glVertex(const Vec3& v, Real dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} + +static inline void glColor(const Vec3& color) { + glColor3f( std::max(0.0f,std::min(1.0f,(float)color.x)), + std::max(0.0f,std::min(1.0f,(float)color.y)), + std::max(0.0f,std::min(1.0f,(float)color.z)) ); +} + +void ParticlePainter::paint() { + if (!mObject) return; + if (mMode == PaintOff) return; + float dx = mLocal->getParent()->getDx(); + mHavePdata = false; + mMaxVal = 0.; + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); // disable depth test for particles, clashes with display plane for regular ones + glDisable(GL_LIGHTING); + + // draw points + if(mLocal->getType() == ParticleBase::VORTEX) { + VortexParticleSystem* vp = (VortexParticleSystem*) mLocal; + glColor3f(1,1,0); + for(int i=0; i<vp->size(); i++) { + if (vp->isActive(i)) { + Vec3 pos = (*vp)[i].pos; + + glPointSize((*vp)[i].sigma); + + glBegin(GL_POINTS); + glVertex(pos, dx); + glEnd(); + } + } + } else if (mLocal->getType() == ParticleBase::FILAMENT) { + // Filaments don't work yet + /*VortexFilamentSystem* fp = (VortexFilamentSystem*) mLocal; + glColor3f(1,1,0); + + for(int i=0; i<fp->segSize(); i++) { + if (!fp->isSegActive(i)) continue; + const VortexRing& r = fp->seg(i); + + glPointSize(1.0); + glBegin(GL_LINES); + for(int j=0; j<r.size(); j++) { + glVertex( (*fp)[r.idx0(j)].pos, dx); + glVertex( (*fp)[r.idx1(j)].pos, dx); + } + glEnd(); + } */ + } else if(mLocal->getType() == ParticleBase::TURBULENCE) { + TurbulenceParticleSystem* vp = (TurbulenceParticleSystem*) mLocal; + glPointSize(2.5); + glColor3f(0,1,0); + glBegin(GL_POINTS); + for(int i=0; i<(int)vp->size(); i++) { + Vec3 pos = (*vp)[i].pos; + glColor((*vp)[i].color); + glVertex(pos, dx); + + } + glEnd(); + + } else if(mLocal->getType() == ParticleBase::PARTICLE) { + paintBasicSys(); +#if 0 + BasicParticleSystem* bp = (BasicParticleSystem*) mLocal; + + // draw other particle data, if available + int pdataId = mMode % (bp->getNumPdata() + 2); + std::ostringstream infoStr; + bool drewPoints = false; + + if( pdataId==0 ) { + // dont draw any points + infoStr << "Off\n"; + drewPoints = true; + } else if( pdataId==1 ) { + // dont draw data, only flags with center below + infoStr << "Drawing center & flags\n"; + } else if (bp->getNumPdata() > 0) { + int pdNum = pdataId-2; // start at 0 + ParticleDataBase* pdb = bp->getPdata(pdNum); + + switch (pdb->getType() ) { + + case ParticleDataBase::DATA_REAL: { + ParticleDataImpl<Real>* pdi = dynamic_cast<ParticleDataImpl<Real>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( pdi->get(i), mMaxVal ); + Real val = pdi->get(i) * scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", real\n"; + } break; + + case ParticleDataBase::DATA_INT: { + ParticleDataImpl<int>* pdi = dynamic_cast<ParticleDataImpl<int>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + Real val = pdi->get(i); + mMaxVal = std::max( val, mMaxVal ); + val *= scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", int\n"; + } break; + + case ParticleDataBase::DATA_VEC3: { + ParticleDataImpl<Vec3>* pdi = dynamic_cast<ParticleDataImpl<Vec3>*>(pdb); + if(!pdi) break; + mHavePdata = true; + glBegin(GL_LINES); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = pdi->get(i) * scale; + glColor3f(0.5,0.0,0); + glVertex(pos, dx); + pos += val; + glColor3f(0.5,1.0,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", vec3\n"; + } break; + + default: { + // skip... + } break; + } + } + + mPdataInfo = infoStr.str(); + // enforce refresh upon change + if(mLastPdata!=pdataId) { + mLastPdata = pdataId; + updateText(); + } + + // otherwise draw center + if(!drewPoints) { + glPointSize(1.5); + glBegin(GL_POINTS); + + for(int i=0; i<(int)bp->size(); i++) { + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + + if(!bp->isActive(i) ) { + glColor3f(1.0, 0., 0.); // deleted, red + } else if(bp->getStatus(i) & ParticleBase::PNEW ) { + glColor3f(0.0, 1.0, 0.); // new, greem + } else { + glColor3f(0, 0.0, 1.0); // regular, blue + } + glVertex(pos, dx); + + } + glEnd(); + } + + // draw basic part sys done +#endif + } + + glPointSize(1.0); + glEnable(GL_DEPTH_TEST); +} + +void ParticlePainter::paintBasicSys() { + BasicParticleSystem* bp = (BasicParticleSystem*) mLocal; + //int dim = mGridRef->getDim(); + + // obtain current plane & draw settings + int dim = mGridRef->getDim(); + Real factor = mGridRef->getMax() / mLocal->getParent()->getGridSize()[dim]; + int plane = factor * mGridRef->getPlane(); + Real scale = getScale(); + float dx = mLocal->getParent()->getDx(); + + // draw other particle data, if available + int pdataId = mMode % (bp->getNumPdata() + 2); + std::ostringstream infoStr; + bool drewPoints = false; + + if( pdataId==0 ) { + // dont draw any points + infoStr << "Off\n"; + drewPoints = true; + } else if( pdataId==1 ) { + // dont draw data, only flags with center below + infoStr << "Drawing center & flags\n"; + } else if (bp->getNumPdata() > 0) { + int pdNum = pdataId-2; // start at 0 + ParticleDataBase* pdb = bp->getPdata(pdNum); + + switch (pdb->getType() ) { + + case ParticleDataBase::DATA_REAL: { + ParticleDataImpl<Real>* pdi = dynamic_cast<ParticleDataImpl<Real>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( pdi->get(i), mMaxVal ); + Real val = pdi->get(i) * scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", real\n"; + } break; + + case ParticleDataBase::DATA_INT: { + ParticleDataImpl<int>* pdi = dynamic_cast<ParticleDataImpl<int>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + Real val = pdi->get(i); + mMaxVal = std::max( val, mMaxVal ); + val *= scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", int\n"; + } break; + + case ParticleDataBase::DATA_VEC3: { + ParticleDataImpl<Vec3>* pdi = dynamic_cast<ParticleDataImpl<Vec3>*>(pdb); + if(!pdi) break; + mHavePdata = true; + + // particle vector data can be drawn in different ways... + mDisplayMode = mDisplayMode%2; + + switch(mDisplayMode) { + case 0: // lines + glBegin(GL_LINES); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = pdi->get(i) * scale; + glColor3f(0.5,0.0,0); + glVertex(pos, dx); + pos += val; + glColor3f(0.5,1.0,0); + glVertex(pos, dx); + } + glEnd(); + break; + case 1: + // colored points + glPointSize(2.0); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = pdi->get(i) * scale; + for(int c=0; c<3; ++c) val[c] = fmod( (Real)val[c], (Real)1.); + + glColor3f(val[0],val[1],val[2]); + glVertex(pos, dx); + //pos += val; + //glColor3f(0.5,1.0,0); + //glVertex(pos, dx); + } + glEnd(); + drewPoints = true; + break; + } + + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", vec3\n"; + } break; + + default: { + // skip... + } break; + } + } + + mPdataInfo = infoStr.str(); + // enforce refresh upon change + if(mLastPdata!=pdataId) { + mLastPdata = pdataId; + updateText(); + } + + // otherwise draw center + if(!drewPoints) { + glPointSize(1.5); + glBegin(GL_POINTS); + + for(int i=0; i<(int)bp->size(); i++) { + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + + if(!bp->isActive(i) ) { + glColor3f(1.0, 0., 0.); // deleted, red + } else if(bp->getStatus(i) & ParticleBase::PNEW ) { + glColor3f(0.0, 1.0, 0.); // new, greem + } else { + glColor3f(0, 0.0, 1.0); // regular, blue + } + glVertex(pos, dx); + + } + glEnd(); + } + + // draw basic part sys done +} + +} // namespace + diff --git a/source/blender/python/manta_full/source/gui/particlepainter.h b/source/blender/python/manta_full/source/gui/particlepainter.h new file mode 100644 index 00000000000..da0a208b3e1 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/particlepainter.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting particle systems + * + ******************************************************************************/ + +#ifndef _PARTICLEPAINTER_H_ +#define _PARTICLEPAINTER_H_ + +#include "painter.h" +#include "particle.h" + +namespace Manta { + +//! Painter object for Particle Systems +class ParticlePainter : public LockedObjPainter { + Q_OBJECT +public: + ParticlePainter(GridPainter<int>* gridRef, QWidget* par = 0); + ~ParticlePainter(); + + void paint(); + void attachWidget(QLayout* layout); + + enum PaintModes { PaintOff=0, PaintVel=1, PaintPos=2 }; + +protected: + std::string getID(); + Real getScale(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + + GridPainter<int>* mGridRef; + ParticleBase* mLocal; + QLabel* mInfo; + + //! for standard particle systems, this is used to select the data channel (display is changed with displayMode below) + int mMode; + //! for BasicParticleSystem , change the way data channels are displayed + int mDisplayMode; + + int mLastPdata; + bool mHavePdata; + Real mMaxVal; + std::string mPdataInfo; + std::map<PbClass*, Real> mValScale; + + void paintBasicSys(); +}; + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/gui/qtmain.cpp b/source/blender/python/manta_full/source/gui/qtmain.cpp new file mode 100644 index 00000000000..64ec6f388e6 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/qtmain.cpp @@ -0,0 +1,150 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * QT threads
+ *
+ ******************************************************************************/
+
+#include "mainwindow.h"
+#include "qtmain.h"
+#include "customctrl.h"
+
+using namespace std;
+
+// execute python script
+// from pymain.cpp
+extern void runScript(vector<string>& args);
+
+namespace Manta {
+
+GuiThread* gGuiThread = NULL;
+MainThread* gMainThread = NULL;
+
+MainThread::MainThread(vector<string>& args) : mFinished(false), mArgs(args) {
+}
+
+void MainThread::run() {
+ runScript(mArgs);
+}
+
+void MainThread::sendAndWait(int e) {
+ mMutex.lock();
+ emit sendToGui(e);
+ while(!mWait.wait(&mMutex, 250))
+ if (gGuiThread->getWindow()->closeRequest()) {
+ mMutex.unlock();
+ throw Error("User interrupt");
+ }
+ mMutex.unlock();
+}
+
+void MainThread::send(int e) {
+ emit sendToGui(e);
+}
+
+void MainThread::killMe() {
+ if (!mFinished) {
+ wait(1000);
+ if (!mFinished) {
+ cout << "worker thread still running, terminate" << endl;
+ terminate();
+ return;
+ }
+ }
+ wait();
+}
+
+void MainThread::wakeUp() {
+ mMutex.lock();
+ mWait.wakeAll();
+ mMutex.unlock();
+}
+
+GuiThread::GuiThread(QApplication& app) : mApp(app), mWnd() {
+}
+
+void GuiThread::sendEvent(int e) {
+ mApp.postEvent(&mWnd, new QEvent((QEvent::Type)e));
+}
+
+void GuiThread::exitApp() {
+ mApp.exit(1);
+}
+
+void guiMain(int argc, char* argv[]) {
+ QApplication app(argc, argv);
+
+ // parse arguments
+ vector<string> args;
+ for (int i=1;i<argc;i++) args.push_back(argv[i]);
+
+ // Show file dialog if no argument is present
+ if (argc <= 1) {
+ QString filename = QFileDialog::getOpenFileName(0, "Open scene file", "", "Python scene files (*.py)");
+ args.push_back(filename.toLatin1().data());
+ }
+
+ GuiThread gui(app);
+ MainThread worker(args);
+
+ gGuiThread = &gui;
+ gMainThread = &worker;
+
+ // connect thread wakeup and termination signals
+ QObject::connect(&worker, SIGNAL(sendToGui(int)), &gui, SLOT(sendEvent(int)));
+ QObject::connect(gui.getWindow(), SIGNAL(wakeMain()), &worker, SLOT(wakeUp()));
+ QObject::connect(gui.getWindow(), SIGNAL(killMain()), &worker, SLOT(killMe()));
+ QObject::connect(gui.getWindow(), SIGNAL(exitApp()), &gui, SLOT(exitApp()));
+ app.setQuitOnLastWindowClosed(true);
+
+ // Start main program threads
+ worker.start();
+ app.exec();
+}
+
+void guiWaitFinish() {
+ gMainThread->setFinished();
+ gMainThread->send((int)MainWnd::EventInstantKill);
+ /*
+ if (gGuiThread->getWindow()->closeRequest())
+ return;
+
+ gMainThread->sendAndWait((int)MainWnd::EventFinalUpdate);
+ gGuiThread->getWindow()->pause();
+ while (gGuiThread->getWindow()->pauseRequest())
+ gMainThread->threadSleep(10); */
+}
+
+//******************************************************************************
+// Python adapter class
+
+
+// external callback functions
+void updateQtGui(bool full, int frame, const string& curPlugin) {
+ if (!gGuiThread->getWindow()->isVisible()) return;
+ if (gGuiThread->getWindow()->closeRequest()) throw Error("User interrupt");
+
+ if (full && frame >= 0) gGuiThread->getWindow()->setFrame(frame);
+ gMainThread->sendAndWait(full ? (int)MainWnd::EventFullUpdate : (int)MainWnd::EventStepUpdate);
+
+ if (gGuiThread->getWindow()->pauseRequest()) {
+ if (!curPlugin.empty()) {
+ cout << "Step: " << curPlugin << endl;
+ }
+ gGuiThread->getWindow()->setPauseStatus(true);
+ while (gGuiThread->getWindow()->pauseRequest()) {
+ gMainThread->threadSleep(10);
+ }
+ if (gGuiThread->getWindow()->closeRequest()) throw Error("User interrupt");
+ gGuiThread->getWindow()->setPauseStatus(false);
+ }
+ gGuiThread->getWindow()->stepReset(full);
+}
+
+} //namespace
diff --git a/source/blender/python/manta_full/source/gui/qtmain.h b/source/blender/python/manta_full/source/gui/qtmain.h new file mode 100644 index 00000000000..7037b2e2cf1 --- /dev/null +++ b/source/blender/python/manta_full/source/gui/qtmain.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT threads + * + ******************************************************************************/ +#ifndef _QTMAIN_H_ +#define _QTMAIN_H_ + +#include <QThread> +#include <QApplication> +#include <vector> +#include <QMutex> +#include <QWaitCondition> +#include "mainwindow.h" +#include "manta.h" + +namespace Manta { + +//! encapsulates GUI thread +class GuiThread : public QObject { + Q_OBJECT +public: + + GuiThread(QApplication& app); + + //! obtain window handle + inline MainWnd* getWindow() { return &mWnd; } + +public slots: + void sendEvent(int e); + void exitApp(); + +protected: + QApplication& mApp; + MainWnd mWnd; +}; + +//! encapsulates working/python thread +class MainThread : public QThread { + Q_OBJECT +public: + MainThread(std::vector<std::string>& args); + + //! send event to GUI and wait for completion + void sendAndWait(int e); + void send(int e); + + //! sleep for given number of milliseconds + inline void threadSleep(int msec) { msleep(msec); } + inline bool isFinished() { return mFinished; } + inline void setFinished() { mFinished = true; } + +public slots: + void wakeUp(); + void killMe(); + +signals: + void sendToGui(int event); + +protected: + QMutex mMutex; + QWaitCondition mWait; + bool mFinished; + std::vector<std::string> mArgs; + void run(); +}; + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/kernel.cpp b/source/blender/python/manta_full/source/kernel.cpp new file mode 100644 index 00000000000..f8fdda8d751 --- /dev/null +++ b/source/blender/python/manta_full/source/kernel.cpp @@ -0,0 +1,36 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Function and macros for defining compution kernels over grids + * + ******************************************************************************/ + +#include "kernel.h" +#include "grid.h" +#include "particle.h" + +namespace Manta { + +KernelBase::KernelBase(const GridBase* base, int bnd) : + maxX (base->getSizeX()-bnd), + maxY (base->getSizeY()-bnd), + maxZ (base->is3D() ? (base->getSizeZ()-bnd) : 1), + minZ (base->is3D() ? bnd : 0), + X (base->getStrideX()), + Y (base->getStrideY()), + Z (base->getStrideZ()), + size (base->getSizeX() * base->getSizeY() * base->getSizeZ()), + threadId(0),threadNum(1) {} + +KernelBase::KernelBase(int sz) : + size(sz), + threadId(0),threadNum(1) {} + + +} // namespace diff --git a/source/blender/python/manta_full/source/kernel.h b/source/blender/python/manta_full/source/kernel.h new file mode 100644 index 00000000000..4c71b090d13 --- /dev/null +++ b/source/blender/python/manta_full/source/kernel.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Function and macros for defining compution kernels over grids + * + ******************************************************************************/ + +#ifndef _KERNEL_H +#define _KERNEL_H + +#ifdef TBB +# include <tbb/blocked_range3d.h> +# include <tbb/blocked_range.h> +# include <tbb/parallel_for.h> +# include <tbb/parallel_reduce.h> +#endif + +#ifdef OPENMP +# include <omp.h> +#endif + +namespace Manta { +// fwd decl +class GridBase; +class ParticleBase; + + +// simple iteration +#define FOR_IJK_BND(grid, bnd) \ + for(int k=((grid).is3D() ? bnd : 0),__kmax=((grid).is3D() ? ((grid).getSizeZ()-bnd) : 1); k<__kmax; k++) \ + for(int j=bnd; j<(grid).getSizeY()-bnd; j++) \ + for(int i=bnd; i<(grid).getSizeX()-bnd; i++) + +#define FOR_IJK_REVERSE(grid) \ + for(int k=(grid).getSizeZ()-1; k>=0; k--) \ + for(int j=(grid).getSizeY()-1; j>=0; j--) \ + for(int i=(grid).getSizeX()-1; i>=0; i--) + +#define FOR_IDX(grid) \ + for(int idx=0, total=(grid).getSizeX()*(grid).getSizeY()*(grid).getSizeZ(); idx<total; idx++) + +#define FOR_IJK(grid) FOR_IJK_BND(grid, 0) + + +struct KernelBase { + int maxX, maxY, maxZ, minZ; + int X, Y, Z; + int size; + //! store thread info for this kernel + int threadId, threadNum; + + KernelBase(int num); + KernelBase(const GridBase* base, int bnd); + //KernelBase(int _maxX, int _maxY, int _maxZ, int _maxC, int _minZ, int _X, int _Y, int _Z); + + // specify in your derived classes: + + // kernel operators + // ijk mode: void operator() (size_t idx) + // idx mode: void operator() (size_t i, size_t j, size_t k) + + // reduce mode: + // void join(classname& other) + // void setup() +}; + +} // namespace + +// Define plugin documentation group +// all kernels will automatically be added to this group +/*! @defgroup Kernels Computation Kernels + */ + +#endif diff --git a/source/blender/python/manta_full/source/levelset.cpp b/source/blender/python/manta_full/source/levelset.cpp new file mode 100644 index 00000000000..eab16ae7e18 --- /dev/null +++ b/source/blender/python/manta_full/source/levelset.cpp @@ -0,0 +1,334 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Levelset + * + ******************************************************************************/ + +#include "levelset.h" +#include "fastmarch.h" +#include "kernel.h" +#include "mcubes.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +//************************************************************************ +// Helper functions and kernels for marching + +static const int FlagInited = FastMarch<FmHeapEntryOut, +1>::FlagInited; + +// neighbor lookup vectors +static const Vec3i neighbors[6] = { Vec3i(-1,0,0), Vec3i(1,0,0), Vec3i(0,-1,0), Vec3i(0,1,0), Vec3i(0,0,-1), Vec3i(0,0,1) }; + +KERNEL(bnd=1) +void InitFmIn (FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType) { + const int idx = flags.index(i,j,k); + const Real v = phi[idx]; + if (v>=0 && (!ignoreWalls || (flags[idx] & obstacleType) == 0)) + fmFlags[idx] = FlagInited; + else + fmFlags[idx] = 0; +} + +KERNEL(bnd=1) +void InitFmOut (FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType) { + const int idx = flags.index(i,j,k); + const Real v = phi[idx]; + if (ignoreWalls) { + fmFlags[idx] = (v<0) ? FlagInited : 0; + if ((flags[idx] & obstacleType) != 0) { + fmFlags[idx] = 0; + phi[idx] = 0; + } + } + else + fmFlags[idx] = (v<0) ? FlagInited : 0; +} + +KERNEL(bnd=1) +void SetUninitialized (Grid<int>& fmFlags, LevelsetGrid& phi, const Real val) { + if (fmFlags(i,j,k) != FlagInited) + phi(i,j,k) = val; +} + +template<bool inward> +inline bool isAtInterface(Grid<int>& fmFlags, LevelsetGrid& phi, const Vec3i& p) { + // check for interface + for (int nb=0; nb<6; nb++) { + const Vec3i pn(p + neighbors[nb]); + if (!fmFlags.isInBounds(pn)) continue; + + if (fmFlags(pn) != FlagInited) continue; + if ((inward && phi(pn) >= 0) || + (!inward && phi(pn) < 0)) return true; + } + return false; +} + +// helper function to compute normal +inline Vec3 getNormal(const Grid<Real>& data, int i, int j, int k) { + if (i > data.getSizeX()-2) i= data.getSizeX()-2; + if (j > data.getSizeY()-2) j= data.getSizeY()-2; + if (k > data.getSizeZ()-2) k= data.getSizeZ()-2; + if (i < 1) i = 1; + if (j < 1) j = 1; + if (k < 1) k = 1; + return Vec3( data(i+1,j ,k ) - data(i-1,j ,k ) , + data(i ,j+1,k ) - data(i ,j-1,k ) , + data(i ,j ,k+1) - data(i ,j ,k-1) ); +} + +//************************************************************************ +// Levelset class def + +LevelsetGrid::LevelsetGrid(FluidSolver* parent, bool show) + : Grid<Real>(parent, show) +{ + mType = (GridType)(TypeLevelset | TypeReal); +} + +Real LevelsetGrid::invalidTimeValue() { + return FastMarch<FmHeapEntryOut, 1>::InvalidTime(); +} + +//! Kernel: perform levelset union +KERNEL(idx) void KnJoin(Grid<Real>& a, const Grid<Real>& b) { + a[idx] = min(a[idx], b[idx]); +} + +void LevelsetGrid::join(const LevelsetGrid& o) { + KnJoin(*this, o); +} + +//! re-init levelset and extrapolate velocities (in & out) +// note - uses flags to identify border (could also be done based on ls values) +void LevelsetGrid::reinitMarching( + FlagGrid& flags, Real maxTime, MACGrid* velTransport, + bool ignoreWalls, bool correctOuterLayer, int obstacleType + , Grid<Real>* normSpeed ) +{ + const int dim = (is3D() ? 3 : 2); + + Grid<int> fmFlags(mParent); + LevelsetGrid& phi = *this; + + FastMarch<FmHeapEntryIn, -1> marchIn (flags, fmFlags, phi, maxTime, NULL, NULL); + + // march inside + InitFmIn (flags, fmFlags, phi, ignoreWalls, obstacleType); + + FOR_IJK_BND(flags, 1) { + if (fmFlags(i,j,k) == FlagInited) continue; + if ((flags(i,j,k) & obstacleType) != 0) continue; + const Vec3i p(i,j,k); + + if(isAtInterface<true>(fmFlags, phi, p)) { + // set value + fmFlags(p) = FlagInited; + + // add neighbors that are not at the interface + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + if ((flags.get(pn) & obstacleType) != 0) continue; + + // check neighbors of neighbor + if (phi(pn) < 0 && !isAtInterface<true>(fmFlags, phi, pn)) { + marchIn.addToList(pn, p); + } + } + } + } + marchIn.performMarching(); + // done with inwards marching + + // now march out... + + // set un initialized regions + SetUninitialized (fmFlags, phi, -maxTime - 1.); + + InitFmOut (flags, fmFlags, phi, ignoreWalls, obstacleType); + + FastMarch<FmHeapEntryOut, +1> marchOut(flags, fmFlags, phi, maxTime, velTransport, normSpeed); + + // NT_DEBUG + if(normSpeed && velTransport) { + FOR_IJK_BND(flags, 1) { + Vec3 vel = velTransport->getCentered(i,j,k); + Vec3 norm = getNormal(phi, i,j,k); normalize(norm); + (*normSpeed)(i,j,k) = dot( norm , vel ); + } + } + + // by default, correctOuterLayer is on + if (correctOuterLayer) { + // normal version, inwards march is done, now add all outside values (0..2] to list + // note, this might move the interface a bit! but keeps a nice signed distance field... + FOR_IJK_BND(flags, 1) { + if ((flags(i,j,k) & obstacleType) != 0) continue; + const Vec3i p(i,j,k); + + // check nbs + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + + if (fmFlags(pn) != FlagInited) continue; + if ((flags.get(pn) & obstacleType) != 0) continue; + + const Real nbPhi = phi(pn); + + // only add nodes near interface, not e.g. outer boundary vs. invalid region + if (nbPhi < 0 && nbPhi >= -2) + marchOut.addToList(p, pn); + } + } + } else { + // alternative version, keep interface, do not distort outer cells + // add all ouside values, but not those at the IF layer + FOR_IJK_BND(flags, 1) { + if ((flags(i,j,k) & obstacleType) != 0) continue; + + // only look at ouside values + const Vec3i p(i,j,k); + if (phi(p) < 0) continue; + + if (isAtInterface<false>(fmFlags, phi, p)) { + // now add all non, interface neighbors + fmFlags(p) = FlagInited; + + // add neighbors that are not at the interface + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + if ((flags.get(pn) & obstacleType) != 0) continue; + + // check neighbors of neighbor + if (phi(pn) > 0 && !isAtInterface<false>(fmFlags, phi, pn)) + marchOut.addToList(pn, p); + } + } + } + } + marchOut.performMarching(); + + // set un initialized regions + SetUninitialized (fmFlags, phi, +maxTime + 1.); + +} + +void LevelsetGrid::initFromFlags(FlagGrid& flags, bool ignoreWalls) { + FOR_IDX(*this) { + if (flags.isFluid(idx) || (ignoreWalls && flags.isObstacle(idx))) + mData[idx] = -0.5; + else + mData[idx] = 0.5; + } +} + +// note - the following functions are experimental, might be removed at some point NT_DEBUG +KERNEL(idx) void knGridRemapLsMask (Grid<Real>& me, Real min, Real max, Real fac) { + me[idx] = (clamp(me[idx], min, max) - min) * fac; + + // now we have 0..1 range, convert to hat around 1/2 + me[idx] = me[idx] * 2.; + if(me[idx]>1.0) me[idx] = 2. - me[idx]; +} +// remap min/max range to hat function around (min+max)/2 +PYTHON void remapLsMask(Grid<Real>& phi, Real min, Real max) { + Real fac = 0.; + if ( fabs(max-min) > VECTOR_EPSILON ) fac = 1. / (max-min); + knGridRemapLsMask(phi, min, max, fac); +} + +//! run marching cubes to create a mesh for the 0-levelset +void LevelsetGrid::createMesh(Mesh& mesh) { + assertMsg(is3D(), "Only 3D grids supported so far"); + + mesh.clear(); + + const Real invalidTime = invalidTimeValue(); + const Real isoValue = 1e-4; + + // create some temp grids + Grid<int> edgeVX(mParent); + Grid<int> edgeVY(mParent); + Grid<int> edgeVZ(mParent); + + for(int i=0; i<mSize.x-1; i++) + for(int j=0; j<mSize.y-1; j++) + for(int k=0; k<mSize.z-1; k++) { + Real value[8] = { get(i,j,k), get(i+1,j,k), get(i+1,j+1,k), get(i,j+1,k), + get(i,j,k+1), get(i+1,j,k+1), get(i+1,j+1,k+1), get(i,j+1,k+1) }; + + // build lookup index, check for invalid times + bool skip = false; + int cubeIdx = 0; + for (int l=0;l<8;l++) { + value[l] *= -1; + if (-value[l] <= invalidTime) + skip = true; + if (value[l] < isoValue) + cubeIdx |= 1<<l; + } + if (skip || (mcEdgeTable[cubeIdx] == 0)) continue; + + // where to look up if this point already exists + int triIndices[12]; + int *eVert[12] = { &edgeVX(i,j,k), &edgeVY(i+1,j,k), &edgeVX(i,j+1,k), &edgeVY(i,j,k), + &edgeVX(i,j,k+1), &edgeVY(i+1,j,k+1), &edgeVX(i,j+1,k+1), &edgeVY(i,j,k+1), + &edgeVZ(i,j,k), &edgeVZ(i+1,j,k), &edgeVZ(i+1,j+1,k), &edgeVZ(i,j+1,k) }; + + const Vec3 pos[9] = { Vec3(i,j,k), Vec3(i+1,j,k), Vec3(i+1,j+1,k), Vec3(i,j+1,k), + Vec3(i,j,k+1), Vec3(i+1,j,k+1), Vec3(i+1,j+1,k+1), Vec3(i,j+1,k+1) }; + + for (int e=0; e<12; e++) { + if (mcEdgeTable[cubeIdx] & (1<<e)) { + // vertex already calculated ? + if (*eVert[e] == 0) { + // interpolate edge + const int e1 = mcEdges[e*2 ]; + const int e2 = mcEdges[e*2+1]; + const Vec3 p1 = pos[ e1 ]; // scalar field pos 1 + const Vec3 p2 = pos[ e2 ]; // scalar field pos 2 + const float valp1 = value[ e1 ]; // scalar field val 1 + const float valp2 = value[ e2 ]; // scalar field val 2 + const float mu = (isoValue - valp1) / (valp2 - valp1); + + // init isolevel vertex + Node vertex; + vertex.pos = p1 + (p2-p1)*mu; + vertex.normal = getNormalized( + getNormal( *this, i+cubieOffsetX[e1], j+cubieOffsetY[e1], k+cubieOffsetZ[e1]) * (1.0-mu) + + getNormal( *this, i+cubieOffsetX[e2], j+cubieOffsetY[e2], k+cubieOffsetZ[e2]) * ( mu)) ; + + triIndices[e] = mesh.addNode(vertex) + 1; + + // store vertex + *eVert[e] = triIndices[e]; + } else { + // retrieve from vert array + triIndices[e] = *eVert[e]; + } + } + } + + // Create the triangles... + for(int e=0; mcTriTable[cubeIdx][e]!=-1; e+=3) { + mesh.addTri( Triangle( triIndices[ mcTriTable[cubeIdx][e+0]] - 1, + triIndices[ mcTriTable[cubeIdx][e+1]] - 1, + triIndices[ mcTriTable[cubeIdx][e+2]] - 1)); + } + } + + //mesh.rebuildCorners(); + //mesh.rebuildLookup(); +} + + +} //namespace diff --git a/source/blender/python/manta_full/source/levelset.h b/source/blender/python/manta_full/source/levelset.h new file mode 100644 index 00000000000..1dd023110ab --- /dev/null +++ b/source/blender/python/manta_full/source/levelset.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Levelset + * + ******************************************************************************/ + +#ifndef _LEVELSET_H_ +#define _LEVELSET_H_ + +#include "grid.h" + +namespace Manta { +class Mesh; + +//! Special function for levelsets +PYTHON class LevelsetGrid : public Grid<Real> { +public: + PYTHON LevelsetGrid(FluidSolver* parent, bool show = true); + + //! reconstruct the levelset using fast marching + PYTHON void reinitMarching(FlagGrid& flags, Real maxTime=4.0, + MACGrid* velTransport=NULL, bool ignoreWalls=false, bool correctOuterLayer=true, + int obstacleType = FlagGrid::TypeObstacle, Grid<Real>* scalarTransport = NULL ); + //! create a triangle mesh from the levelset isosurface + PYTHON void createMesh(Mesh& mesh); + + //! union with another levelset + PYTHON void join(const LevelsetGrid& o); + + //! initialize levelset from flags (+/- 0.5 heaviside) + PYTHON void initFromFlags(FlagGrid& flags, bool ignoreWalls=false); + + static Real invalidTimeValue(); +}; + +} //namespace +#endif diff --git a/source/blender/python/manta_full/source/mesh.cpp b/source/blender/python/manta_full/source/mesh.cpp new file mode 100644 index 00000000000..1291ec98a95 --- /dev/null +++ b/source/blender/python/manta_full/source/mesh.cpp @@ -0,0 +1,775 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Meshes + * + * note: this is only a temporary solution, details are bound to change + * long term goal is integration with Split&Merge code by Wojtan et al. + * + ******************************************************************************/ + +#include "mesh.h" +#include "integrator.h" +#include "fileio.h" +#include "kernel.h" +#include "shapes.h" +#include <stack> + +using namespace std; +namespace Manta { + +Mesh::Mesh(FluidSolver* parent) : PbClass(parent) { +} + +Mesh::~Mesh() { +} + +Mesh* Mesh::clone() { + Mesh* nm = new Mesh(mParent); + *nm = *this; + nm->setName(getName()); + return nm; +} + +Real Mesh::computeCenterOfMass(Vec3& cm) const { + + // use double precision for summation, otherwise too much error accumulation + double vol=0; + Vector3D<double> cmd(0.0); + for(size_t tri=0; tri < mTris.size(); tri++) { + Vector3D<double> p1(toVec3d(getNode(tri,0))); + Vector3D<double> p2(toVec3d(getNode(tri,1))); + Vector3D<double> p3(toVec3d(getNode(tri,2))); + + double cvol = dot(cross(p1,p2),p3) / 6.0; + cmd += (p1+p2+p3) * (cvol/3.0); + vol += cvol; + } + if (vol != 0.0) cmd /= vol; + + cm = toVec3(cmd); + return (Real) vol; +} + +void Mesh::clear() { + mNodes.clear(); + mTris.clear(); + mCorners.clear(); + m1RingLookup.clear(); + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->resize(0); + for(size_t i=0; i<mTriChannels.size(); i++) + mTriChannels[i]->resize(0); +} + +Mesh& Mesh::operator=(const Mesh& o) { + // wipe current data + clear(); + if (mNodeChannels.size() != o.mNodeChannels.size() || + mTriChannels.size() != o.mTriChannels.size()) + errMsg("can't copy mesh, channels not identical"); + mNodeChannels.clear(); + mTriChannels.clear(); + + // copy corner, nodes, tris + mCorners = o.mCorners; + mNodes = o.mNodes; + mTris = o.mTris; + m1RingLookup = o.m1RingLookup; + + // copy channels + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i] = o.mNodeChannels[i]; + for(size_t i=0; i<o.mTriChannels.size(); i++) + mTriChannels[i] = o.mTriChannels[i]; + + return *this; +} + +void Mesh::load(string name, bool append) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".obj") + readObjFile(name, this, append); + else + errMsg("file '" + name +"' filetype not supported"); + + rebuildCorners(); + rebuildLookup(); +} + +void Mesh::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".obj") + writeObjFile(name, this); + else if (ext == ".gz") + writeBobjFile(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +void Mesh::fromShape(Shape& shape, bool append) { + if (!append) + clear(); + shape.generateMesh(this); +} + + +//! do a quick check whether a rebuild is necessary, and if yes do rebuild +void Mesh::rebuildQuickCheck() { + if(mCorners.size() != 3*mTris.size()) + rebuildCorners(); + if(m1RingLookup.size() != mNodes.size()) + rebuildLookup(); +} + +void Mesh::rebuildCorners(int from, int to) { + mCorners.resize(3*mTris.size()); + if (to < 0) to = mTris.size(); + + // fill in basic info + for (int tri=from; tri<to; tri++) { + for (int c=0; c<3; c++) { + const int idx = tri*3+c; + mCorners[idx].tri = tri; + mCorners[idx].node = mTris[tri].c[c]; + mCorners[idx].next = 3*tri+((c+1)%3); + mCorners[idx].prev = 3*tri+((c+2)%3); + mCorners[idx].opposite = -1; + } + } + + // set opposite info + int maxc = to*3; + for (int c=from*3; c<maxc; c++) { + int next = mCorners[mCorners[c].next].node; + int prev = mCorners[mCorners[c].prev].node; + + // find corner with same next/prev nodes + for (int c2=c+1; c2<maxc; c2++) { + int next2 = mCorners[mCorners[c2].next].node; + if (next2 != next && next2 != prev) continue; + int prev2 = mCorners[mCorners[c2].prev].node; + if (prev2 != next && prev2 != prev) continue; + + // found + mCorners[c].opposite = c2; + mCorners[c2].opposite = c; + break; + } + if (mCorners[c].opposite < 0) { + // didn't find opposite +// errMsg("can't rebuild corners, index without an opposite"); + } + } + + rebuildChannels(); +} + +void Mesh::rebuildLookup(int from, int to) { + if (from==0 && to<0) m1RingLookup.clear(); + m1RingLookup.resize(mNodes.size()); + if (to<0) to = mTris.size(); + from *=3; to *= 3; + for (int i=from; i< to; i++) { + const int node = mCorners[i].node; + m1RingLookup[node].nodes.insert(mCorners[mCorners[i].next].node); + m1RingLookup[node].nodes.insert(mCorners[mCorners[i].prev].node); + m1RingLookup[node].tris.insert(mCorners[i].tri); + } +} + +void Mesh::rebuildChannels() { + for(size_t i=0; i<mTriChannels.size(); i++) + mTriChannels[i]->resize(mTris.size()); + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->resize(mNodes.size()); +} + +KERNEL(pts) returns(vector<Vec3> u(size)) +vector<Vec3> KnAdvectMeshInGrid(vector<Node>& nodes, const FlagGrid& flags, const MACGrid& vel, const Real dt) { + if (nodes[idx].flags & Mesh::NfFixed) + u[idx] = _0; + else if (!flags.isInBounds(nodes[idx].pos,1)) + u[idx] = _0; + else + u[idx] = vel.getInterpolated(nodes[idx].pos) * dt; +} + +// advection plugin +void Mesh::advectInGrid(FlagGrid& flaggrid, MACGrid& vel, int integrationMode) { + KnAdvectMeshInGrid kernel(mNodes, flaggrid, vel, getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void Mesh::scale(Vec3 s) { + for (size_t i=0; i<mNodes.size(); i++) + mNodes[i].pos *= s; +} + +void Mesh::offset(Vec3 o) { + for (size_t i=0; i<mNodes.size(); i++) + mNodes[i].pos += o; +} + +void Mesh::removeTri(int tri) { + // delete triangles by overwriting them with elements from the end of the array. + if(tri!=(int)mTris.size()-1) { + // if this is the last element, and it is marked for deletion, + // don't waste cycles transfering data to itself, + // and DEFINITELY don't transfer .opposite data to other, untainted triangles. + + // old corners hold indices on the end of the corners array + // new corners holds indices in the new spot in the middle of the array + Corner* oldcorners[3]; + Corner* newcorners[3]; + int oldtri = mTris.size()-1; + for (int c=0; c<3; c++) { + oldcorners[c] = &corners(oldtri,c); + newcorners[c] = &corners(tri, c); + } + + // move the position of the triangle + mTris[tri] = mTris[oldtri]; + + // 1) update c.node, c.opposite (c.next and c.prev should be fine as they are) + for (int c=0; c<3; c++) { + newcorners[c]->node = mTris[tri].c[c]; + newcorners[c]->opposite = oldcorners[c]->opposite; + } + + // 2) c.opposite.opposite = c + for (int c=0; c<3; c++) { + if (newcorners[c]->opposite>=0) + mCorners[newcorners[c]->opposite].opposite = 3*tri+c; + } + + // update tri lookup + for (int c=0; c<3; c++) { + int node = mTris[tri].c[c]; + m1RingLookup[node].tris.erase(oldtri); + m1RingLookup[node].tris.insert(tri); + } + } + + // transfer tri props + for(size_t p=0; p < mTriChannels.size(); p++) + mTriChannels[p]->remove(tri); + + // pop the triangle and corners out of the vector + mTris.pop_back(); + mCorners.resize(mTris.size()*3); +} + +void Mesh::removeNodes(const vector<int>& deletedNodes) { + // After we delete the nodes that are marked for removal, + // the size of mNodes will be the current size - the size of the deleted array. + // We are going to move the elements at the end of the array + // (everything with an index >= newsize) + // to the deleted spots. + // We have to map all references to the last few nodes to their new locations. + int newsize = (int)(mNodes.size() - deletedNodes.size()); + + vector<int> new_index (deletedNodes.size()); + int di,ni; + for(ni=0; ni<(int)new_index.size(); ni++) + new_index[ni] = 0; + for(di=0; di<(int)deletedNodes.size(); di++) { + if(deletedNodes[di] >= newsize) + new_index[deletedNodes[di]-newsize] = -1; // tag this node as invalid + } + for(di=0,ni=0; ni<(int)new_index.size(); ni++,di++) { + // we need to find a valid node to move + // we marked invalid nodes in the earlier loop with a (-1), + // so pick anything but those + while(ni<(int)new_index.size() && new_index[ni]==-1) + ni++; + + if(ni>=(int)new_index.size()) + break; + + // next we need to find a valid spot to move the node to. + // we iterate through deleted[] until we find a valid spot + while(di<(int)new_index.size() && deletedNodes[di]>=newsize) + di++; + + // now we assign the valid node to the valid spot + new_index[ni] = deletedNodes[di]; + } + + // Now we have a map of valid indices. + // we move node[newsize+i] to location new_index[i]. + // We ignore the nodes with a -1 index, because they should not be moved. + for(int i=0; i<(int)new_index.size(); i++) { + if(new_index[i]!=-1) + mNodes[ new_index[i] ] = mNodes[ newsize+i ]; + } + mNodes.resize(newsize); + + // handle vertex properties + for (size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->renumber(new_index, newsize); + + // finally, we reconnect everything that used to point to this vertex. + for(size_t tri=0, n=0; tri<mTris.size(); tri++) { + for (int c=0; c<3; c++,n++) { + if (mCorners[n].node >= newsize) { + int newindex = new_index[mCorners[n].node - newsize]; + mCorners[n].node = newindex; + mTris[mCorners[n].tri].c[c] = newindex; + } + } + } + + // renumber 1-ring + for(int i=0; i<(int)new_index.size(); i++) { + if(new_index[i]!=-1) { + m1RingLookup[new_index[i]].nodes.swap(m1RingLookup[newsize+i].nodes); + m1RingLookup[new_index[i]].tris.swap(m1RingLookup[newsize+i].tris); + } + } + m1RingLookup.resize(newsize); + vector<int> reStack(new_index.size()); + for(int i=0; i<newsize; i++) { + set<int>& cs = m1RingLookup[i].nodes; + int reNum = 0; + // find all nodes > newsize + set<int>::reverse_iterator itend = cs.rend(); + for (set<int>::reverse_iterator it = cs.rbegin(); it != itend; ++it) { + if (*it < newsize) break; + reStack[reNum++] = *it; + } + // kill them and insert shifted values + if (reNum > 0) { + cs.erase(cs.find(reStack[reNum-1]), cs.end()); + for (int j=0; j<reNum; j++) { + cs.insert(new_index[reStack[j]-newsize]); +#ifdef DEBUG + if (new_index[reStack[j]-newsize] == -1) + errMsg("invalid node present in 1-ring set"); +#endif + } + } + } +} + +void Mesh::mergeNode(int node, int delnode) { + set<int>& ring = m1RingLookup[delnode].nodes; + for(set<int>::iterator it = ring.begin(); it != ring.end(); ++it) { + m1RingLookup[*it].nodes.erase(delnode); + if (*it != node) { + m1RingLookup[*it].nodes.insert(node); + m1RingLookup[node].nodes.insert(*it); + } + } + set<int>& ringt = m1RingLookup[delnode].tris; + for(set<int>::iterator it = ringt.begin(); it != ringt.end(); ++it) { + const int t = *it; + for (int c=0; c<3; c++) { + if (mCorners[3*t+c].node == delnode) { + mCorners[3*t+c].node = node; + mTris[t].c[c] = node; + } + } + m1RingLookup[node].tris.insert(t); + } + for(size_t i=0; i<mNodeChannels.size(); i++) { + // weight is fixed to 1/2 for now + mNodeChannels[i]->mergeWith(node, delnode, 0.5); + } +} + +void Mesh::removeTriFromLookup(int tri) { + for(int c=0; c<3; c++) { + int node = mTris[tri].c[c]; + m1RingLookup[node].tris.erase(tri); + } +} + +void Mesh::addCorner(Corner a) { + mCorners.push_back(a); +} + +int Mesh::addTri(Triangle a) { + mTris.push_back(a); + for (int c=0;c<3;c++) { + int node = a.c[c]; + int nextnode = a.c[(c+1)%3]; + if ((int)m1RingLookup.size() <= node) m1RingLookup.resize(node+1); + if ((int)m1RingLookup.size() <= nextnode) m1RingLookup.resize(nextnode+1); + m1RingLookup[node].nodes.insert(nextnode); + m1RingLookup[nextnode].nodes.insert(node); + m1RingLookup[node].tris.insert(mTris.size()-1); + } + return mTris.size()-1; +} + +int Mesh::addNode(Node a) { + mNodes.push_back(a); + if (m1RingLookup.size() < mNodes.size()) + m1RingLookup.resize(mNodes.size()); + return mNodes.size()-1; +} + +void Mesh::computeVertexNormals() { + for (size_t i=0; i<mNodes.size(); i++) { + mNodes[i].normal = 0.0; + } + for (size_t t=0; t<mTris.size(); t++) { + Vec3 p0 = getNode(t,0), p1 = getNode(t,1), p2 = getNode(t,2); + Vec3 n0 = p0-p1, n1 = p1-p2, n2 = p2-p0; + Real l0 = normSquare(n0), l1 = normSquare(n1), l2 = normSquare(n2); + + Vec3 nm = cross(n0,n1); + + mNodes[mTris[t].c[0]].normal += nm * (1.0 / (l0*l2)); + mNodes[mTris[t].c[1]].normal += nm * (1.0 / (l0*l1)); + mNodes[mTris[t].c[2]].normal += nm * (1.0 / (l1*l2)); + } + for (size_t i=0; i<mNodes.size(); i++) { + normalize(mNodes[i].normal); + } +} + +void Mesh::fastNodeLookupRebuild(int corner) { + int node = mCorners[corner].node; + m1RingLookup[node].nodes.clear(); + m1RingLookup[node].tris.clear(); + int start = mCorners[corner].prev; + int current = start; + do { + m1RingLookup[node].nodes.insert(mCorners[current].node); + m1RingLookup[node].tris.insert(mCorners[current].tri); + current = mCorners[mCorners[current].opposite].next; + if (current < 0) + errMsg("Can't use fastNodeLookupRebuild on incomplete surfaces"); + } while (current != start); +} + +void Mesh::sanityCheck(bool strict, vector<int>* deletedNodes, map<int,bool>* taintedTris) { + const int nodes = numNodes(), tris = numTris(), corners = 3*tris; + for(size_t i=0; i<mNodeChannels.size(); i++) { + if (mNodeChannels[i]->size() != nodes) + errMsg("Node channel size mismatch"); + } + for(size_t i=0; i<mTriChannels.size(); i++) { + if (mTriChannels[i]->size() != tris) + errMsg("Tri channel size mismatch"); + } + if ((int)m1RingLookup.size() != nodes) + errMsg("1Ring size wrong"); + for(size_t t=0; t<mTris.size(); t++) { + if (taintedTris && taintedTris->find(t) != taintedTris->end()) continue; + for (int c=0; c<3; c++) { + int corner = t*3+c; + int node = mTris[t].c[c]; + int next = mTris[t].c[(c+1)%3]; + int prev = mTris[t].c[(c+2)%3]; + int rnext = mCorners[corner].next; + int rprev = mCorners[corner].prev; + int ro = mCorners[corner].opposite; + if (node < 0 || node >= nodes || next < 0 || next >= nodes || prev < 0 || prev >= nodes) + errMsg("invalid node entry"); + if (mCorners[corner].node != node || mCorners[corner].tri != (int)t) + errMsg("invalid basic corner entry"); + if (rnext < 0 || rnext >= corners || rprev < 0 || rprev >= corners || ro >= corners) + errMsg("invalid corner links"); + if (mCorners[rnext].node != next || mCorners[rprev].node != prev) + errMsg("invalid corner next/prev"); + if (strict && ro < 0) + errMsg("opposite missing"); + if (mCorners[ro].opposite != corner) + errMsg("invalid opposite ref"); + set<int>& rnodes = m1RingLookup[node].nodes; + set<int>& rtris = m1RingLookup[node].tris; + if (rnodes.find(next) == rnodes.end() || rnodes.find(prev) == rnodes.end()) { + debMsg("Tri "<< t << " " << node << " " << next << " " << prev , 1); + for(set<int>::iterator it= rnodes.begin(); it != rnodes.end(); ++it) + debMsg( *it , 1); + errMsg("node missing in 1ring"); + } + if (rtris.find(t) == rtris.end()) { + debMsg("Tri "<< t << " " << node , 1); + errMsg("tri missing in 1ring"); + } + } + } + for (int n=0; n<nodes; n++) { + bool docheck=true; + if (deletedNodes) + for (size_t e=0; e<deletedNodes->size(); e++) + if ((*deletedNodes)[e] == n) docheck=false;; + + if (docheck) { + set<int>& sn = m1RingLookup[n].nodes; + set<int>& st = m1RingLookup[n].tris; + set<int> sn2; + + for (set<int>::iterator it=st.begin(); it != st.end(); ++it) { + bool found = false; + for (int c=0; c<3; c++) { + if (mTris[*it].c[c] == n) + found = true; + else + sn2.insert(mTris[*it].c[c]); + } + if (!found) { + cout << *it << " " << n << endl; + for (int c=0; c<3; c++) cout << mTris[*it].c[c] << endl; + errMsg("invalid triangle in 1ring"); + } + if (taintedTris && taintedTris->find(*it) != taintedTris->end()) { + cout << *it << endl; + errMsg("tainted tri still is use"); + } + } + if (sn.size() != sn2.size()) + errMsg("invalid nodes in 1ring"); + for (set<int>::iterator it=sn.begin(), it2=sn2.begin(); it != sn.end(); ++it,++it2) { + if (*it != *it2) { + cout << "Node " << n << ": " << *it << " vs " << *it2 << endl; + errMsg("node ring mismatch"); + } + } + } + } +} + + void Mesh::SDFKernel(const int* partStart, const int* partLen, CVec3Ptr pos, CVec3Ptr normal, float* sdf, Vec3i gridRes, int intRadius, float safeRadius2, float cutoff2, float isigma2) + { + for (int cnt_x(0); cnt_x < gridRes[0]; ++cnt_x) { + for (int cnt_y(0); cnt_y < gridRes[1]; ++cnt_y) { + for (int cnt_z(0); cnt_z < gridRes[2]; ++cnt_z) { + // cell index, center + Vec3i cell = Vec3i(cnt_x, cnt_y, cnt_z); + if (cell.x >= gridRes.x || cell.y >= gridRes.y || cell.z >= gridRes.z) return; + Vec3 cpos = Vec3(cell.x + 0.5f, cell.y + 0.5f, cell.z + 0.5f); + float sum = 0.0f; + float dist = 0.0f; + + // query cells within block radius + Vec3i minBlock = Vec3i(max(cell.x - intRadius,0), max(cell.y - intRadius,0), max(cell.z - intRadius,0)); + Vec3i maxBlock = Vec3i(min(cell.x + intRadius, gridRes.x - 1), min(cell.y + intRadius, gridRes.y - 1), min(cell.z + intRadius, gridRes.z - 1)); + for (int i=minBlock.x; i<=maxBlock.x; i++) + for (int j=minBlock.y; j<=maxBlock.y; j++) + for (int k=minBlock.z; k<=maxBlock.z; k++) { + // test if block is within radius + Vec3 d = Vec3(cell.x-i, cell.y-j, cell.z-k); + float normSqr = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; + if (normSqr > safeRadius2) continue; + + // find source cell, and divide it into thread blocks + int block = i + gridRes.x * (j + gridRes.y * k); + int slen = partLen[block]; + if (slen == 0) continue; + int start = partStart[block]; + + // process sources + for(int s=0; s<slen; s++) { + + // actual sdf kernel + Vec3 r = cpos - pos.get(start+s); + float normSqr = r[0]*r[0] + r[1]*r[1] + r[2]*r[2]; + float r2 = normSqr; + if (r2 < cutoff2) { + float w = expf(-r2*isigma2); + sum += w; + dist += dot(normal.get(start+s), r) * w; + } + } + } + // writeback + if (sum > 0.0f) + sdf[cell.x + gridRes.x * (cell.y + gridRes.y * cell.z)] = dist / sum; + } + } + } + } + + inline int _cIndex(const Vec3& pos, const Vec3i& s) { + Vec3i p = toVec3i(pos); + if (p.x < 0 || p.y < 0 || p.z < 0 || p.x >= s.x || p.y >= s.y || p.z >= s.z) return -1; + return p.x + s.x * (p.y + s.y * p.z); + } + + //! Kernel: Apply a shape to a grid, setting value inside + KERNEL template<class T> + void ApplyMeshToGrid (Grid<T>* grid, Grid<Real> sdf, T value, FlagGrid* respectFlags) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (sdf(i,j,k) < 0) + { + (*grid)(i,j,k) = value; + } + } + + void Mesh::applyToGrid(GridBase* grid, FlagGrid* respectFlags, float cutoff) { + FluidSolver dummy(grid->getSize()); + LevelsetGrid mesh_sdf(&dummy, false); + meshSDF(*this, mesh_sdf, 2., _args.get<float>("cutoff")); + + if (grid->getType() & GridBase::TypeInt) + ApplyMeshToGrid<int> ((Grid<int>*)grid, mesh_sdf, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyMeshToGrid<Real> ((Grid<Real>*)grid, mesh_sdf, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyMeshToGrid<Vec3> ((Grid<Vec3>*)grid, mesh_sdf, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGrid(): unknown grid type"); + } + + void Mesh::meshSDF(Mesh& mesh, LevelsetGrid & levelset, float sigma, float cutoff) + { + if (cutoff<0) cutoff = 2*sigma; + float maxEdgeLength = 0.75; + float numSamplesPerCell = 0.75; + + Vec3i gridRes = levelset.getSize(); + Vec3 mult = toVec3(gridRes) / toVec3(mesh.getParent()->getGridSize()); + + // prepare center values + std::vector<Vec3> center; + std::vector<Vec3> normals; + short bigEdges(0); + std::vector<Vec3> samplePoints; + for(size_t i=0; i<mesh.numTris(); i++){ + center.push_back(Vec3(mesh.getFaceCenter(i) * mult)); + normals.push_back(mesh.getFaceNormal(i)); + //count big, stretched edges + bigEdges = 0; + for (short edge(0); edge <3; ++edge){ + if(norm(mesh.getEdge(i,edge)) > maxEdgeLength){ + bigEdges += 1 << edge; + } + } + if(bigEdges > 0){ + samplePoints.clear(); + short iterA, pointA, iterB, pointB; + int numSamples0 = norm(mesh.getEdge(i,1)) * numSamplesPerCell; + int numSamples1 = norm(mesh.getEdge(i,2)) * numSamplesPerCell; + int numSamples2 = norm(mesh.getEdge(i,0)) * numSamplesPerCell; + if(! (bigEdges & (1 << 0))){ + //loop through 0,1 + iterA = numSamples1; + pointA = 0; + iterB = numSamples2; + pointB = 1; + } + else if(! (bigEdges & (1 << 1))){ + //loop through 1,2 + iterA = numSamples2; + pointA = 1; + iterB = numSamples0; + pointB = 2; + + } + else{ + //loop through 2,0 + iterA = numSamples0; + pointA = 2; + iterB = numSamples1; + pointB = 0; + } + + short axisA(0), axisB(1); + float u(0.),v(0.),w(0.); //barycentric uvw coords + Vec3 samplePoint,normal; + for (int sample0(0); sample0 < iterA; ++sample0){ + u = float(1. * sample0 / iterA); + for (int sample1(0); sample1 < iterB; ++sample1){ + v = float(1. * sample1 / iterB); + w = 1 - u - v; + if (w < 0.) + continue; + samplePoint = mesh.getNode(i,pointA) * mult * u + + mesh.getNode(i,pointB) * mult * v + + mesh.getNode(i,(3 - pointA - pointB)) * mult * w; + samplePoints.push_back(samplePoint); + normal = mesh.getFaceNormal(i); + normals.push_back(normal); + } + } + center.insert(center.end(), samplePoints.begin(), samplePoints.end()); + } + } + // prepare grid + const int numCells = gridRes.x * gridRes.y * gridRes.z; + vector<Real> gridDev(numCells); + for (int i=0; i<numCells; i++) + gridDev[i] = -cutoff; + + // 1. count sources per cell + vector<int> srcPerCell(numCells); + for (size_t i=0; i<center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell >= 0) + srcPerCell[cell]++; + } + + // 2. create start index lookup + vector<int> srcCellStart(numCells); + int cnt=0; + for (int i=0; i<numCells; i++) { + srcCellStart[i] = cnt; + cnt += srcPerCell[i]; + } + + // 3. reorder nodes + CVec3Array reorderPos(center.size()); + CVec3Array reorderNormal(center.size()); + { + vector<int> curSrcCell(numCells); + for (int i=0; i<(int)center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell < 0) continue; + int idx = srcCellStart[cell] + curSrcCell[cell]; + reorderPos.set(idx, center[i]); + reorderNormal.set(idx, normals[i]); + curSrcCell[cell]++; + } + } + + // construct parameters + float safeRadius = cutoff + sqrt(3.0)*0.5; + float safeRadius2 = safeRadius*safeRadius; + float cutoff2 = cutoff*cutoff; + float isigma2 = 1.0/(sigma*sigma); + int intRadius = (int)(cutoff+0.5); + + SDFKernel(srcCellStart.data(), srcPerCell.data(), + reorderPos.data(), reorderNormal.data(), + gridDev.data(), + Vec3i(gridRes.x, gridRes.y, gridRes.z), intRadius, safeRadius2, cutoff2, isigma2); + + for (int i=0;i<numCells; i++) + levelset[i] = gridDev[i]; + + // floodfill outside + std::stack<Vec3i> outside; + FOR_IJK(levelset) { + if (levelset(i,j,k) >= cutoff-1.0f) + outside.push(Vec3i(i,j,k)); + } + while(!outside.empty()) { + Vec3i c = outside.top(); + outside.pop(); + levelset(c) = cutoff; + if (c.x > 0 && levelset(c.x-1, c.y, c.z) < 0) outside.push(Vec3i(c.x-1,c.y,c.z)); + if (c.y > 0 && levelset(c.x, c.y-1, c.z) < 0) outside.push(Vec3i(c.x,c.y-1,c.z)); + if (c.z > 0 && levelset(c.x, c.y, c.z-1) < 0) outside.push(Vec3i(c.x,c.y,c.z-1)); + if (c.x < levelset.getSizeX()-1 && levelset(c.x+1, c.y, c.z) < 0) outside.push(Vec3i(c.x+1,c.y,c.z)); + if (c.y < levelset.getSizeY()-1 && levelset(c.x, c.y+1, c.z) < 0) outside.push(Vec3i(c.x,c.y+1,c.z)); + if (c.z < levelset.getSizeZ()-1 && levelset(c.x, c.y, c.z+1) < 0) outside.push(Vec3i(c.x,c.y,c.z+1)); + }; + } + +} //namespace diff --git a/source/blender/python/manta_full/source/mesh.h b/source/blender/python/manta_full/source/mesh.h new file mode 100644 index 00000000000..e09c85801b7 --- /dev/null +++ b/source/blender/python/manta_full/source/mesh.h @@ -0,0 +1,257 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Meshes + * + * note: this is only a temporary solution, details are bound to change + * long term goal is integration with Split&Merge code by Wojtan et al. + * + ******************************************************************************/ + +#ifndef _MESH_H +#define _MESH_H + +#include <vector> +#include "manta.h" +#include "grid.h" +#include "vectorbase.h" +#include <set> +namespace Manta { + +// fwd decl +class FlagGrid; +class MACGrid; +class Shape; + +//! Node position and flags +struct Node { + Node() : flags(0), pos(Vec3::Zero), normal(Vec3::Zero) {} + Node(const Vec3& p) : flags(0), pos(p) {} + int flags; + Vec3 pos, normal; +}; + +//! Carries indices of its nodes +struct Triangle { + Triangle() : flags(0) { c[0] = c[1] = c[2] = 0; } + Triangle(int n0, int n1, int n2) : flags(0) { c[0]=n0; c[1]=n1; c[2]=n2; } + + int c[3]; + int flags; +}; + +//! For fast access to nodes and neighboring triangles +struct Corner { + Corner() : tri(-1), node(-1), opposite(-1), next(-1), prev(-1) {}; + Corner(int t, int n) : tri(t), node(n), opposite(-1), next(-1), prev(-1) {} + + int tri; + int node; + int opposite; + int next; + int prev; +}; + +//! Base class for mesh data channels (texture coords, vorticity, ...) +struct NodeChannel { + virtual ~NodeChannel() {}; + virtual void resize(int num) = 0; + virtual int size() = 0; + virtual NodeChannel* clone() = 0; + + virtual void addInterpol(int a, int b, Real alpha) = 0; + virtual void mergeWith(int node, int delnode, Real alpha) = 0; + virtual void renumber(const std::vector<int>& newIndex, int newsize) = 0; +}; + +//! Node channel using only a vector +template<class T> +struct SimpleNodeChannel : public NodeChannel { + SimpleNodeChannel() {}; + SimpleNodeChannel(const SimpleNodeChannel<T>& a) : data(a.data) {} + void resize(int num) { data.resize(num); } + virtual int size() { return data.size(); } + virtual void renumber(const std::vector<int>& newIndex, int newsize); + + //virtual void addSplit(int from, Real alpha) { data.push_back(data[from]); } + + std::vector<T> data; +}; + +//! Base class for mesh data channels (texture coords, vorticity, ...) +struct TriChannel { + virtual ~TriChannel() {}; + virtual void resize(int num) = 0; + virtual TriChannel* clone() = 0; + virtual int size() = 0; + + virtual void addNew() = 0; + virtual void addSplit(int from, Real alpha) = 0; + virtual void remove(int tri) = 0; +}; + +//! Tri channel using only a vector +template<class T> +struct SimpleTriChannel : public TriChannel { + SimpleTriChannel() {}; + SimpleTriChannel(const SimpleTriChannel<T>& a) : data(a.data) {} + void resize(int num) { data.resize(num); } + void remove(int tri) { if (tri!=(int)data.size()-1) data[tri] = *data.rbegin(); data.pop_back(); } + virtual int size() { return data.size(); } + + virtual void addSplit(int from, Real alpha) { data.push_back(data[from]); } + virtual void addNew() { data.push_back(T()); } + + std::vector<T> data; +}; + +struct OneRing { + OneRing() {} + std::set<int> nodes; + std::set<int> tris; +}; + +/*!adapted from Cudatools.h +*/ +struct CVec3Ptr { + float *x, *y, *z; + inline Vec3 get(int i) const { return Vec3(x[i],y[i],z[i]); }; + inline void set(int i, const Vec3& v) { x[i]=v.x; y[i]=v.y; z[i]=v.z; }; +}; + +struct CVec3Array { + CVec3Array(int sz) { + x.resize(sz); + y.resize(sz); + z.resize(sz); + } + CVec3Array(const std::vector<Vec3>& v) { + x.resize(v.size()); + y.resize(v.size()); + z.resize(v.size()); + for (size_t i=0; i<v.size(); i++) { + x[i] = v[i].x; + y[i] = v[i].y; + z[i] = v[i].z; + } + } + CVec3Ptr data() { + CVec3Ptr a = { x.data(), y.data(), z.data()}; + return a; + } + inline const Vec3 operator[](int idx) const { return Vec3((Real)x[idx], (Real)y[idx], (Real)z[idx]); } + inline void set(int idx, const Vec3& v) { x[idx] = v.x; y[idx] = v.y; z[idx] = v.z; } + inline int size() { return x.size(); } + std::vector<float> x, y, z; +}; + +//! Triangle mesh class +/*! note: this is only a temporary solution, details are bound to change + long term goal is integration with Split&Merge code by Wojtan et al.*/ +PYTHON class Mesh : public PbClass { +public: + PYTHON Mesh(FluidSolver* parent); + virtual ~Mesh(); + virtual Mesh* clone(); + + enum NodeFlags { NfNone = 0, NfFixed = 1, NfMarked = 2, NfKillme = 4, NfCollide = 8 }; + enum FaceFlags { FfNone = 0, FfDoubled = 1, FfMarked = 2 }; + enum MeshType { TypeNormal = 0, TypeVortexSheet }; + + virtual MeshType getType() { return TypeNormal; } + + void clear(); + Real computeCenterOfMass(Vec3& cm) const; + void computeVertexNormals(); + + // plugins + PYTHON void load (std::string name, bool append = false); + PYTHON void fromShape (Shape& shape, bool append = false); + PYTHON void save (std::string name); + PYTHON void advectInGrid(FlagGrid& flaggrid, MACGrid& vel, int integrationMode); + PYTHON void scale(Vec3 s); + PYTHON void offset(Vec3 o); + + // ops + Mesh& operator=(const Mesh& o); + + // accessors + inline int numTris() const { return mTris.size(); } + inline int numNodes() const { return mNodes.size(); } + inline int numTriChannels() const { return mTriChannels.size(); } + inline int numNodeChannels() const { return mNodeChannels.size(); } + + inline Triangle& tris(int i) { return mTris[i]; } + inline Node& nodes(int i) { return mNodes[i]; } + inline Corner& corners(int tri, int c) { return mCorners[tri*3+c]; } + inline Corner& corners(int c) { return mCorners[c]; } + inline NodeChannel* nodeChannel(int i) { return mNodeChannels[i]; } + inline TriChannel* triChannel(int i) { return mTriChannels[i]; } + + inline bool isNodeFixed(int n) { return mNodes[n].flags & NfFixed; } + inline bool isTriangleFixed(int t) { return (mNodes[mTris[t].c[0]].flags & NfFixed) || (mNodes[mTris[t].c[1]].flags & NfFixed) || (mNodes[mTris[t].c[2]].flags & NfFixed); } + + inline const Vec3 getNode(int tri, int c) const { return mNodes[mTris[tri].c[c]].pos; } + inline Vec3& getNode(int tri, int c) { return mNodes[mTris[tri].c[c]].pos; } + inline const Vec3 getEdge(int tri, int e) const { return getNode(tri,(e+1)%3) - getNode(tri,e); } + inline OneRing& get1Ring(int node) { return m1RingLookup[node]; } + inline Real getFaceArea(int t) { Vec3 c0 = mNodes[mTris[t].c[0]].pos; return 0.5*norm(cross(mNodes[mTris[t].c[1]].pos - c0, mNodes[mTris[t].c[2]].pos - c0)); } + inline Vec3 getFaceNormal(int t) { Vec3 c0 = mNodes[mTris[t].c[0]].pos; return getNormalized(cross(mNodes[mTris[t].c[1]].pos - c0, mNodes[mTris[t].c[2]].pos - c0)); } + inline Vec3 getFaceCenter(int t) { return (mNodes[mTris[t].c[0]].pos + mNodes[mTris[t].c[1]].pos + mNodes[mTris[t].c[2]].pos) / 3.0; } + inline std::vector<Node>& getNodeData() { return mNodes; } + + void mergeNode(int node, int delnode); + int addNode(Node a); + int addTri(Triangle a); + void addCorner(Corner a); + void removeTri(int tri); + void removeTriFromLookup(int tri); + void removeNodes(const std::vector<int>& deletedNodes); + void rebuildCorners(int from=0, int to=-1); + void rebuildLookup(int from=0, int to=-1); + void rebuildQuickCheck(); + void fastNodeLookupRebuild(int corner); + void sanityCheck(bool strict=true, std::vector<int>* deletedNodes=0, std::map<int,bool>* taintedTris=0); + + void addTriChannel(TriChannel* c) { mTriChannels.push_back(c); rebuildChannels(); } + void addNodeChannel(NodeChannel* c) { mNodeChannels.push_back(c); rebuildChannels(); } + void SDFKernel(const int* partStart, const int* partLen, CVec3Ptr pos, CVec3Ptr normal, float* sdf, Vec3i gridRes, int intRadius, float safeRadius2, float cutoff2, float isigma2); + PYTHON void meshSDF(Mesh& mesh, LevelsetGrid& levelset, float sigma, float cutoff=-1); + PYTHON void applyToGrid(GridBase* grid, FlagGrid* respectFlags=0, float cutoff=-1); + +protected: + void rebuildChannels(); + + std::vector<Node> mNodes; + std::vector<Triangle> mTris; + std::vector<Corner> mCorners; + std::vector<NodeChannel*> mNodeChannels; + std::vector<TriChannel*> mTriChannels; + std::vector<OneRing> m1RingLookup; +}; + + + + +// *************************************************************************************************************** +// Implementation + +template<class T> +void SimpleNodeChannel<T>::renumber(const std::vector<int>& newIndex, int newsize) { + for(size_t i=0; i<newIndex.size(); i++) { + if(newIndex[i]!=-1) + data[newIndex[i]] = data[newsize+i]; + } + data.resize(newsize); +} + + + +} //namespace +#endif diff --git a/source/blender/python/manta_full/source/movingobs.cpp b/source/blender/python/manta_full/source/movingobs.cpp new file mode 100644 index 00000000000..227c074c823 --- /dev/null +++ b/source/blender/python/manta_full/source/movingobs.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Moving obstacles + * + ******************************************************************************/ + +#include "movingobs.h" +#include "commonkernels.h" +#include "randomstream.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// MovingObs class members + +int MovingObstacle::sIDcnt = 10; + +MovingObstacle::MovingObstacle (FluidSolver* parent, int emptyType) + : PbClass(parent), mEmptyType(emptyType) +{ + mID = 1<<sIDcnt; + sIDcnt++; + if (sIDcnt > 15) + errMsg ("currently only 5 separate moving obstacles supported (are you generating them in a loop?)"); +} + +void MovingObstacle::add(Shape* shape) { + mShapes.push_back(shape); +} + +void MovingObstacle::projectOutside(FlagGrid& flags, BasicParticleSystem& parts) { + LevelsetGrid levelset(mParent,false); + Grid<Vec3> gradient(mParent); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + // build levelset gradient + GradientOp(gradient, levelset); + + parts.projectOutside(gradient); +} + +void MovingObstacle::moveLinear(Real t, Real t0, Real t1, Vec3 p0, Vec3 p1, FlagGrid& flags, MACGrid& vel, bool smooth) { + Real alpha = (t-t0)/(t1-t0); + if (alpha >= 0 && alpha <= 1) { + Vec3 v = (p1-p0) / ((t1-t0)* getParent()->getDt() ); + + // ease in and out + if (smooth) { + v *= 6.0f*(alpha-square(alpha)); + alpha = square(alpha)*(3.0f-2.0f*alpha); + } + + Vec3 pos = alpha * p1 + (1.0f-alpha) * p0; + for (size_t i=0; i<mShapes.size(); i++) + mShapes[i]->setCenter(pos); + + // reset flags + FOR_IDX(flags) { + if ((flags[idx] & mID) != 0) + flags[idx] = mEmptyType; + } + // apply new flags + for (size_t i=0; i<mShapes.size(); i++) { + mShapes[i]->_args.clear(); + mShapes[i]->_args.add("value", FlagGrid::TypeObstacle | mID); + mShapes[i]->applyToGrid(&flags, 0); + } + // apply velocities + FOR_IJK_BND(flags,1) { + bool cur = (flags(i,j,k) & mID) != 0; + if (cur || (flags(i-1,j,k) & mID) != 0) vel(i,j,k).x = v.x; + if (cur || (flags(i,j-1,k) & mID) != 0) vel(i,j,k).y = v.y; + if (cur || (flags(i,j,k-1) & mID) != 0) vel(i,j,k).z = v.z; + } + } +} + + +} //namespace diff --git a/source/blender/python/manta_full/source/movingobs.h b/source/blender/python/manta_full/source/movingobs.h new file mode 100644 index 00000000000..fdaf8a66a03 --- /dev/null +++ b/source/blender/python/manta_full/source/movingobs.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * moving obstacles + * + ******************************************************************************/ + +#ifndef _MOVINGOBS_H +#define _MOVINGOBS_H + +#include "shapes.h" +#include "particle.h" + +namespace Manta { + +//! Moving obstacle composed of basic shapes +PYTHON class MovingObstacle : public PbClass { +public: + PYTHON MovingObstacle(FluidSolver* parent, int emptyType=FlagGrid::TypeEmpty); + + PYTHON void add(Shape* shape); + //! If t in [t0,t1], apply linear motion path from p0 to p1 + PYTHON void moveLinear(Real t, Real t0, Real t1, Vec3 p0, Vec3 p1, FlagGrid& flags, MACGrid& vel, bool smooth=true); + //! Compute levelset, and project FLIP particles outside obstacles + PYTHON void projectOutside(FlagGrid& flags, BasicParticleSystem& flip); + +protected: + std::vector<Shape*> mShapes; + int mEmptyType; + int mID; + static int sIDcnt; +}; + + +} //namespace +#endif diff --git a/source/blender/python/manta_full/source/noisefield.cpp b/source/blender/python/manta_full/source/noisefield.cpp new file mode 100644 index 00000000000..50d4b713af7 --- /dev/null +++ b/source/blender/python/manta_full/source/noisefield.cpp @@ -0,0 +1,296 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * Noise field
+ *
+ ******************************************************************************/
+
+#include "noisefield.h"
+#include "randomstream.h"
+#include "grid.h"
+
+using namespace std;
+
+//*****************************************************************************
+// Wavelet noise
+
+#if FLOATINGPOINT_PRECISION==1
+#define TILENAME "waveletNoiseTile.bin"
+#else
+#define TILENAME "waveletNoiseTileD.bin"
+#endif
+
+namespace Manta {
+
+int WaveletNoiseField::randomSeed = 13322223;
+Real* WaveletNoiseField::mNoiseTile = NULL;
+
+static Real _aCoeffs[32] = {
+ 0.000334,-0.001528, 0.000410, 0.003545,-0.000938,-0.008233, 0.002172, 0.019120,
+ -0.005040,-0.044412, 0.011655, 0.103311,-0.025936,-0.243780, 0.033979, 0.655340,
+ 0.655340, 0.033979,-0.243780,-0.025936, 0.103311, 0.011655,-0.044412,-0.005040,
+ 0.019120, 0.002172,-0.008233,-0.000938, 0.003546, 0.000410,-0.001528, 0.000334};
+
+void WaveletNoiseField::downsample(Real *from, Real *to, int n, int stride){
+ const Real *a = &_aCoeffs[16];
+ for (int i = 0; i < n / 2; i++) {
+ to[i * stride] = 0;
+ for (int k = 2 * i - 16; k < 2 * i + 16; k++) {
+ to[i * stride] += a[k - 2 * i] * from[modFast128(k) * stride];
+ }
+ }
+}
+
+static Real _pCoeffs[4] = {0.25, 0.75, 0.75, 0.25};
+
+void WaveletNoiseField::upsample(Real *from, Real *to, int n, int stride) {
+ const Real *pp = &_pCoeffs[1];
+
+ for (int i = 0; i < n; i++) {
+ to[i * stride] = 0;
+ for (int k = i / 2 - 1 ; k < i / 2 + 3; k++) {
+ to[i * stride] += 0.5 * pp[k - i / 2] * from[modSlow(k, n / 2) * stride];
+ } // new */
+ }
+}
+
+WaveletNoiseField::WaveletNoiseField(FluidSolver* parent, int fixedSeed, int loadFromFile) :
+ PbClass(parent), mPosOffset(0.), mPosScale(1.), mValOffset(0.), mValScale(1.), mClamp(false),
+ mClampNeg(0), mClampPos(1), mTimeAnim(0), mGsInvX(0), mGsInvY(0), mGsInvZ(0)
+{
+ Real scale = 1.0/parent->getGridSize().max();
+ mGsInvX = scale;
+ mGsInvY = scale;
+ mGsInvZ = parent->is3D() ? scale : 1;
+
+ // use global random seed with offset if none is given
+ if (fixedSeed==-1) {
+ fixedSeed = randomSeed + 123;
+ }
+ RandomStream randStreamPos(fixedSeed);
+ mSeedOffset = Vec3( randStreamPos.getVec3Norm() );
+
+ generateTile( loadFromFile );
+};
+
+string WaveletNoiseField::toString() {
+ std::ostringstream out;
+ out << "NoiseField: name '"<<mName<<"' "<<
+ " pos off="<<mPosOffset<<" scale="<<mPosScale<<
+ " val off="<<mValOffset<<" scale="<<mValScale<<
+ " clamp ="<<mClamp<<" val="<<mClampNeg<<" to "<<mClampPos<<
+ " timeAni ="<<mTimeAnim<<
+ " gridInv ="<<Vec3(mGsInvX,mGsInvY,mGsInvZ) ;
+ return out.str();
+}
+
+void WaveletNoiseField::generateTile( int loadFromFile) {
+ // generate tile
+ const int n = NOISE_TILE_SIZE;
+ const int n3 = n*n*n, n3d=n3*3;
+
+ if(mNoiseTile) return;
+ Real *noise3 = new Real[n3d];
+ if(loadFromFile) {
+ FILE* fp = fopen(TILENAME,"rb");
+ if(fp) {
+ fread(noise3, sizeof(Real), n3d, fp);
+ fclose(fp);
+ debMsg("noise tile loaded from file! " , 1);
+ mNoiseTile = noise3;
+ return;
+ }
+ }
+
+ debMsg("generating 3x " << n << "^3 noise tile " , 1);
+ Real *temp13 = new Real[n3d];
+ Real *temp23 = new Real[n3d];
+
+ // initialize
+ for (int i = 0; i < n3d; i++) {
+ temp13[i] = temp23[i] =
+ noise3[i] = 0.;
+ }
+
+ // Step 1. Fill the tile with random numbers in the range -1 to 1.
+ RandomStream randStreamTile ( randomSeed );
+ for (int i = 0; i < n3d; i++) {
+ //noise3[i] = (randStream.getReal() + randStream2.getReal()) -1.; // produces repeated values??
+ noise3[i] = randStreamTile.getRandNorm(0,1);
+ }
+
+ // Steps 2 and 3. Downsample and upsample the tile
+ for (int tile=0; tile < 3; tile++) {
+ for (int iy = 0; iy < n; iy++)
+ for (int iz = 0; iz < n; iz++) {
+ const int i = iy * n + iz*n*n + tile*n3;
+ downsample(&noise3[i], &temp13[i], n, 1);
+ upsample (&temp13[i], &temp23[i], n, 1);
+ }
+ for (int ix = 0; ix < n; ix++)
+ for (int iz = 0; iz < n; iz++) {
+ const int i = ix + iz*n*n + tile*n3;
+ downsample(&temp23[i], &temp13[i], n, n);
+ upsample (&temp13[i], &temp23[i], n, n);
+ }
+ for (int ix = 0; ix < n; ix++)
+ for (int iy = 0; iy < n; iy++) {
+ const int i = ix + iy*n + tile*n3;
+ downsample(&temp23[i], &temp13[i], n, n*n);
+ upsample (&temp13[i], &temp23[i], n, n*n);
+ }
+ }
+
+ // Step 4. Subtract out the coarse-scale contribution
+ for (int i = 0; i < n3d; i++) {
+ noise3[i] -= temp23[i];
+ }
+
+ // Avoid even/odd variance difference by adding odd-offset version of noise to itself.
+ int offset = n / 2;
+ if (offset % 2 == 0) offset++;
+
+ if (n != 128) errMsg("WaveletNoise::Fast 128 mod used, change for non-128 resolution");
+
+ int icnt=0;
+ for (int tile=0; tile<3; tile++)
+ for (int ix = 0; ix < n; ix++)
+ for (int iy = 0; iy < n; iy++)
+ for (int iz = 0; iz < n; iz++) {
+ temp13[icnt] = noise3[modFast128(ix+offset) + modFast128(iy+offset)*n + modFast128(iz+offset)*n*n + tile*n3];
+ icnt++;
+ }
+
+
+ for (int i = 0; i < n3d; i++) {
+ noise3[i] += temp13[i];
+ }
+
+ mNoiseTile = noise3;
+ delete[] temp13;
+ delete[] temp23;
+
+ if(loadFromFile) {
+ FILE* fp = fopen(TILENAME,"wb");
+ if(fp) {
+ fwrite(noise3, sizeof(Real), n3d, fp);
+ fclose(fp);
+ debMsg( "saved to file! " , 1);
+ }
+ }
+}
+
+
+
+void WaveletNoiseField::downsampleNeumann(const Real *from, Real *to, int n, int stride)
+{
+ // if these values are not local incorrect results are generated
+ static const Real *const aCoCenter= &_aCoeffs[16];
+ for (int i = 0; i < n / 2; i++) {
+ to[i * stride] = 0;
+ for (int k = 2 * i - 16; k < 2 * i + 16; k++) {
+ // handle boundary
+ Real fromval;
+ if (k < 0) {
+ fromval = from[0];
+ } else if(k > n - 1) {
+ fromval = from[(n - 1) * stride];
+ } else {
+ fromval = from[k * stride];
+ }
+ to[i * stride] += aCoCenter[k - 2 * i] * fromval;
+ }
+ }
+}
+
+void WaveletNoiseField::upsampleNeumann(const Real *from, Real *to, int n, int stride) {
+ static const Real *const pp = &_pCoeffs[1];
+ for (int i = 0; i < n; i++) {
+ to[i * stride] = 0;
+ for (int k = i / 2 - 1 ; k < i / 2 + 3; k++) {
+ Real fromval;
+ if(k>n/2-1) {
+ fromval = from[(n/2-1) * stride];
+ } else if(k < 0) {
+ fromval = from[0];
+ } else {
+ fromval = from[k * stride];
+ }
+ to[i * stride] += 0.5 * pp[k - i / 2] * fromval;
+ }
+ }
+}
+
+void WaveletNoiseField::computeCoefficients(Grid<Real>& input, Grid<Real>& tempIn1, Grid<Real>& tempIn2)
+{
+ // generate tile
+ const int sx = input.getSizeX();
+ const int sy = input.getSizeY();
+ const int sz = input.getSizeZ();
+ const int n3 = sx*sy*sz;
+ // just for compatibility with wavelet turb code
+ Real *temp13 = &tempIn1(0,0,0);
+ Real *temp23 = &tempIn2(0,0,0);
+ Real *noise3 = &input(0,0,0);
+
+ // clear grids
+ for (int i = 0; i < n3; i++) {
+ temp13[i] = temp23[i] = 0.f;
+ }
+
+ // Steps 2 and 3. Downsample and upsample the tile
+ for (int iz = 0; iz < sz; iz++)
+ for (int iy = 0; iy < sy; iy++)
+ {
+ const int i = iz*sx*sy + iy*sx;
+ downsampleNeumann(&noise3[i], &temp13[i], sx, 1 );
+ upsampleNeumann (&temp13[i], &temp23[i], sx, 1);
+ }
+
+ for (int iz = 0; iz < sz; iz++)
+ for (int ix = 0; ix < sx; ix++)
+ {
+ const int i = iz*sx*sy + ix;
+ downsampleNeumann(&temp23[i], &temp13[i], sy, sx );
+ upsampleNeumann (&temp13[i], &temp23[i], sy, sx );
+ }
+
+ if(input.is3D()) {
+ for (int iy = 0; iy < sy; iy++)
+ for (int ix = 0; ix < sx; ix++)
+ {
+ const int i = iy*sx+ix;
+ downsampleNeumann(&temp23[i], &temp13[i], sz, sy*sx );
+ upsampleNeumann (&temp13[i], &temp23[i], sz, sy*sx );
+ }
+ }
+
+ // Step 4. Subtract out the coarse-scale contribution
+ for (int i = 0; i < n3; i++) {
+ Real residual = noise3[i] - temp23[i];
+ temp13[i] = sqrtf( fabs(residual) );
+ }
+
+ // copy back, and compute actual weight for wavelet turbulence...
+ Real smoothingFactor = 1./6.;
+ if(!input.is3D()) smoothingFactor = 1./4.;
+ FOR_IJK_BND(input,1) {
+ // apply some brute force smoothing
+ Real res = temp13[k*sx*sy+j*sx+i-1] + temp13[k*sx*sy+j*sx+i+1];
+ res += temp13[k*sx*sy+j*sx+i-sx] + temp13[k*sx*sy+j*sx+i+sx];
+ if( input.is3D()) res += temp13[k*sx*sy+j*sx+i-sx*sy] + temp13[k*sx*sy+j*sx+i+sx*sy];
+ input(i,j,k) = res * smoothingFactor;
+ }
+}
+
+
+
+
+
+}
diff --git a/source/blender/python/manta_full/source/noisefield.h b/source/blender/python/manta_full/source/noisefield.h new file mode 100644 index 00000000000..0ee00cb2ba2 --- /dev/null +++ b/source/blender/python/manta_full/source/noisefield.h @@ -0,0 +1,393 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * Wavelet noise field
+ *
+ ******************************************************************************/
+
+#ifndef _NOISEFIELD_H_
+#define _NOISEFIELD_H_
+
+#include "vectorbase.h"
+#include "manta.h"
+
+namespace Manta {
+
+#define NOISE_TILE_SIZE 128
+
+// wrapper for a parametrized field of wavelet noise
+PYTHON(name=NoiseField)
+class WaveletNoiseField : public PbClass {
+ public:
+ PYTHON WaveletNoiseField( FluidSolver* parent, int fixedSeed=-1 , int loadFromFile=false );
+ ~WaveletNoiseField() {
+ if(mNoiseTile) { delete mNoiseTile; mNoiseTile=NULL; }
+ };
+
+ //! evaluate noise
+ inline Real evaluate(Vec3 pos, int tile=0);
+ //! evaluate noise as a vector
+ inline Vec3 evaluateVec(Vec3 pos, int tile=0);
+ //! evaluate curl noise
+ inline Vec3 evaluateCurl(Vec3 pos);
+
+ //! direct data access
+ Real* data() { return mNoiseTile; }
+
+ //! compute wavelet decomposition of an input grid (stores residual coefficients)
+ static void computeCoefficients(Grid<Real>& input, Grid<Real>& tempIn1, Grid<Real>& tempIn2);
+
+ // helper
+ std::string toString();
+
+ // texcoord position and scale
+ PYTHON(name=posOffset) Vec3 mPosOffset;
+ PYTHON(name=posScale) Vec3 mPosScale;
+ // value offset & scale
+ PYTHON(name=valOffset) Real mValOffset;
+ PYTHON(name=valScale) Real mValScale;
+ // clamp? (default 0-1)
+ PYTHON(name=clamp) bool mClamp;
+ PYTHON(name=clampNeg) Real mClampNeg;
+ PYTHON(name=clampPos) Real mClampPos;
+ // animated over time
+ PYTHON(name=timeAnim) Real mTimeAnim;
+
+ protected:
+ // noise evaluation functions
+ static inline Real WNoiseDx (const Vec3& p, Real *data);
+ static inline Vec3 WNoiseVec(const Vec3& p, Real *data);
+ static inline Real WNoise (const Vec3& p, Real *data);
+
+ // helpers for tile generation , for periodic 128 grids only
+ static void downsample(Real *from, Real *to, int n, int stride);
+ static void upsample (Real *from, Real *to, int n, int stride);
+
+ // for grids with arbitrary sizes, and neumann boundary conditions
+ static void downsampleNeumann(const Real *from, Real *to, int n, int stride);
+ static void upsampleNeumann (const Real *from, Real *to, int n, int stride);
+
+ static inline int modSlow(int x, int n) { int m = x % n; return (m<0) ? m+n : m; }
+ // warning - noiseTileSize has to be 128^3!
+ #define modFast128(x) ((x) & 127)
+
+ inline Real getTime() { return mParent->getTime() * mParent->getDx() * mTimeAnim; }
+
+ // pre-compute tile data for wavelet noise
+ void generateTile( int loadFromFile );
+
+ // animation over time
+ // grid size normalization (inverse size)
+ Real mGsInvX, mGsInvY, mGsInvZ;
+ // random offset into tile to simulate different random seeds
+ Vec3 mSeedOffset;
+
+ static Real* mNoiseTile;
+ // global random seed storage
+ static int randomSeed;
+};
+
+
+
+// **************************************************************************
+// Implementation
+
+#define ADD_WEIGHTED(x,y,z)\
+ weight = 1.0f;\
+ xC = modFast128(midX + (x));\
+ weight *= w[0][(x) + 1];\
+ yC = modFast128(midY + (y));\
+ weight *= w[1][(y) + 1];\
+ zC = modFast128(midZ + (z));\
+ weight *= w[2][(z) + 1];\
+ result += weight * data[(zC * NOISE_TILE_SIZE + yC) * NOISE_TILE_SIZE + xC];
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// derivatives of 3D noise - unrolled for performance
+//////////////////////////////////////////////////////////////////////////////////////////
+inline Real WaveletNoiseField::WNoiseDx(const Vec3& p, Real *data) {
+ Real w[3][3], t, result = 0;
+
+ // Evaluate quadratic B-spline basis functions
+ int midX = (int)ceil(p[0] - 0.5f);
+ t = midX - (p[0] - 0.5f);
+ w[0][0] = -t;
+ w[0][2] = (1.f - t);
+ w[0][1] = 2.0f * t - 1.0f;
+
+ int midY = (int)ceil(p[1] - 0.5f);
+ t = midY - (p[1] - 0.5f);
+ w[1][0] = t * t * 0.5f;
+ w[1][2] = (1.f - t) * (1.f - t) *0.5f;
+ w[1][1] = 1.f - w[1][0] - w[1][2];
+
+ int midZ = (int)ceil(p[2] - 0.5f);
+ t = midZ - (p[2] - 0.5f);
+ w[2][0] = t * t * 0.5f;
+ w[2][2] = (1.f - t) * (1.f - t) *0.5f;
+ w[2][1] = 1.f - w[2][0] - w[2][2];
+
+ // Evaluate noise by weighting noise coefficients by basis function values
+ int xC, yC, zC;
+ Real weight = 1;
+
+ ADD_WEIGHTED(-1,-1, -1); ADD_WEIGHTED( 0,-1, -1); ADD_WEIGHTED( 1,-1, -1);
+ ADD_WEIGHTED(-1, 0, -1); ADD_WEIGHTED( 0, 0, -1); ADD_WEIGHTED( 1, 0, -1);
+ ADD_WEIGHTED(-1, 1, -1); ADD_WEIGHTED( 0, 1, -1); ADD_WEIGHTED( 1, 1, -1);
+
+ ADD_WEIGHTED(-1,-1, 0); ADD_WEIGHTED( 0,-1, 0); ADD_WEIGHTED( 1,-1, 0);
+ ADD_WEIGHTED(-1, 0, 0); ADD_WEIGHTED( 0, 0, 0); ADD_WEIGHTED( 1, 0, 0);
+ ADD_WEIGHTED(-1, 1, 0); ADD_WEIGHTED( 0, 1, 0); ADD_WEIGHTED( 1, 1, 0);
+
+ ADD_WEIGHTED(-1,-1, 1); ADD_WEIGHTED( 0,-1, 1); ADD_WEIGHTED( 1,-1, 1);
+ ADD_WEIGHTED(-1, 0, 1); ADD_WEIGHTED( 0, 0, 1); ADD_WEIGHTED( 1, 0, 1);
+ ADD_WEIGHTED(-1, 1, 1); ADD_WEIGHTED( 0, 1, 1); ADD_WEIGHTED( 1, 1, 1);
+
+ return result;
+}
+
+inline Real WaveletNoiseField::WNoise(const Vec3& p, Real *data) {
+ Real w[3][3], t, result = 0;
+
+ // Evaluate quadratic B-spline basis functions
+ int midX = (int)ceilf(p[0] - 0.5f);
+ t = midX - (p[0] - 0.5f);
+ w[0][0] = t * t * 0.5f;
+ w[0][2] = (1.f - t) * (1.f - t) *0.5f;
+ w[0][1] = 1.f - w[0][0] - w[0][2];
+
+ int midY = (int)ceilf(p[1] - 0.5f);
+ t = midY - (p[1] - 0.5f);
+ w[1][0] = t * t * 0.5f;
+ w[1][2] = (1.f - t) * (1.f - t) *0.5f;
+ w[1][1] = 1.f - w[1][0] - w[1][2];
+
+ int midZ = (int)ceilf(p[2] - 0.5f);
+ t = midZ - (p[2] - 0.5f);
+ w[2][0] = t * t * 0.5f;
+ w[2][2] = (1.f - t) * (1.f - t) *0.5f;
+ w[2][1] = 1.f - w[2][0] - w[2][2];
+
+ // Evaluate noise by weighting noise coefficients by basis function values
+ int xC, yC, zC;
+ Real weight = 1;
+
+ ADD_WEIGHTED(-1,-1, -1); ADD_WEIGHTED( 0,-1, -1); ADD_WEIGHTED( 1,-1, -1);
+ ADD_WEIGHTED(-1, 0, -1); ADD_WEIGHTED( 0, 0, -1); ADD_WEIGHTED( 1, 0, -1);
+ ADD_WEIGHTED(-1, 1, -1); ADD_WEIGHTED( 0, 1, -1); ADD_WEIGHTED( 1, 1, -1);
+
+ ADD_WEIGHTED(-1,-1, 0); ADD_WEIGHTED( 0,-1, 0); ADD_WEIGHTED( 1,-1, 0);
+ ADD_WEIGHTED(-1, 0, 0); ADD_WEIGHTED( 0, 0, 0); ADD_WEIGHTED( 1, 0, 0);
+ ADD_WEIGHTED(-1, 1, 0); ADD_WEIGHTED( 0, 1, 0); ADD_WEIGHTED( 1, 1, 0);
+
+ ADD_WEIGHTED(-1,-1, 1); ADD_WEIGHTED( 0,-1, 1); ADD_WEIGHTED( 1,-1, 1);
+ ADD_WEIGHTED(-1, 0, 1); ADD_WEIGHTED( 0, 0, 1); ADD_WEIGHTED( 1, 0, 1);
+ ADD_WEIGHTED(-1, 1, 1); ADD_WEIGHTED( 0, 1, 1); ADD_WEIGHTED( 1, 1, 1);
+
+ return result;
+}
+
+
+
+#define ADD_WEIGHTEDX(x,y,z)\
+ weight = dw[0][(x) + 1] * w[1][(y) + 1] * w[2][(z) + 1];\
+ result += weight * neighbors[x + 1][y + 1][z + 1];
+
+#define ADD_WEIGHTEDY(x,y,z)\
+ weight = w[0][(x) + 1] * dw[1][(y) + 1] * w[2][(z) + 1];\
+ result += weight * neighbors[x + 1][y + 1][z + 1];
+
+#define ADD_WEIGHTEDZ(x,y,z)\
+ weight = w[0][(x) + 1] * w[1][(y) + 1] * dw[2][(z) + 1];\
+ result += weight * neighbors[x + 1][y + 1][z + 1];
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// compute all derivatives in at once
+//////////////////////////////////////////////////////////////////////////////////////////
+inline Vec3 WaveletNoiseField::WNoiseVec(const Vec3& p, Real *data)
+{
+ Vec3 final(0.);
+ Real w[3][3];
+ Real dw[3][3];
+ Real result = 0;
+ int xC, yC, zC;
+ Real weight;
+
+ int midX = (int)ceil(p[0] - 0.5f);
+ int midY = (int)ceil(p[1] - 0.5f);
+ int midZ = (int)ceil(p[2] - 0.5f);
+
+ Real t0 = midX - (p[0] - 0.5f);
+ Real t1 = midY - (p[1] - 0.5f);
+ Real t2 = midZ - (p[2] - 0.5f);
+
+ // precache all the neighbors for fast access
+ Real neighbors[3][3][3];
+ for (int z = -1; z <=1; z++)
+ for (int y = -1; y <= 1; y++)
+ for (int x = -1; x <= 1; x++)
+ {
+ xC = modFast128(midX + (x));
+ yC = modFast128(midY + (y));
+ zC = modFast128(midZ + (z));
+ neighbors[x + 1][y + 1][z + 1] = data[zC * NOISE_TILE_SIZE * NOISE_TILE_SIZE + yC * NOISE_TILE_SIZE + xC];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // evaluate splines
+ ///////////////////////////////////////////////////////////////////////////////////////
+ dw[0][0] = -t0;
+ dw[0][2] = (1.f - t0);
+ dw[0][1] = 2.0f * t0 - 1.0f;
+
+ dw[1][0] = -t1;
+ dw[1][2] = (1.0f - t1);
+ dw[1][1] = 2.0f * t1 - 1.0f;
+
+ dw[2][0] = -t2;
+ dw[2][2] = (1.0f - t2);
+ dw[2][1] = 2.0f * t2 - 1.0f;
+
+ w[0][0] = t0 * t0 * 0.5f;
+ w[0][2] = (1.f - t0) * (1.f - t0) *0.5f;
+ w[0][1] = 1.f - w[0][0] - w[0][2];
+
+ w[1][0] = t1 * t1 * 0.5f;
+ w[1][2] = (1.f - t1) * (1.f - t1) *0.5f;
+ w[1][1] = 1.f - w[1][0] - w[1][2];
+
+ w[2][0] = t2 * t2 * 0.5f;
+ w[2][2] = (1.f - t2) * (1.f - t2) *0.5f;
+ w[2][1] = 1.f - w[2][0] - w[2][2];
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // x derivative
+ ///////////////////////////////////////////////////////////////////////////////////////
+ result = 0.0f;
+ ADD_WEIGHTEDX(-1,-1, -1); ADD_WEIGHTEDX( 0,-1, -1); ADD_WEIGHTEDX( 1,-1, -1);
+ ADD_WEIGHTEDX(-1, 0, -1); ADD_WEIGHTEDX( 0, 0, -1); ADD_WEIGHTEDX( 1, 0, -1);
+ ADD_WEIGHTEDX(-1, 1, -1); ADD_WEIGHTEDX( 0, 1, -1); ADD_WEIGHTEDX( 1, 1, -1);
+
+ ADD_WEIGHTEDX(-1,-1, 0); ADD_WEIGHTEDX( 0,-1, 0); ADD_WEIGHTEDX( 1,-1, 0);
+ ADD_WEIGHTEDX(-1, 0, 0); ADD_WEIGHTEDX( 0, 0, 0); ADD_WEIGHTEDX( 1, 0, 0);
+ ADD_WEIGHTEDX(-1, 1, 0); ADD_WEIGHTEDX( 0, 1, 0); ADD_WEIGHTEDX( 1, 1, 0);
+
+ ADD_WEIGHTEDX(-1,-1, 1); ADD_WEIGHTEDX( 0,-1, 1); ADD_WEIGHTEDX( 1,-1, 1);
+ ADD_WEIGHTEDX(-1, 0, 1); ADD_WEIGHTEDX( 0, 0, 1); ADD_WEIGHTEDX( 1, 0, 1);
+ ADD_WEIGHTEDX(-1, 1, 1); ADD_WEIGHTEDX( 0, 1, 1); ADD_WEIGHTEDX( 1, 1, 1);
+ final[0] = result;
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // y derivative
+ ///////////////////////////////////////////////////////////////////////////////////////
+ result = 0.0f;
+ ADD_WEIGHTEDY(-1,-1, -1); ADD_WEIGHTEDY( 0,-1, -1); ADD_WEIGHTEDY( 1,-1, -1);
+ ADD_WEIGHTEDY(-1, 0, -1); ADD_WEIGHTEDY( 0, 0, -1); ADD_WEIGHTEDY( 1, 0, -1);
+ ADD_WEIGHTEDY(-1, 1, -1); ADD_WEIGHTEDY( 0, 1, -1); ADD_WEIGHTEDY( 1, 1, -1);
+
+ ADD_WEIGHTEDY(-1,-1, 0); ADD_WEIGHTEDY( 0,-1, 0); ADD_WEIGHTEDY( 1,-1, 0);
+ ADD_WEIGHTEDY(-1, 0, 0); ADD_WEIGHTEDY( 0, 0, 0); ADD_WEIGHTEDY( 1, 0, 0);
+ ADD_WEIGHTEDY(-1, 1, 0); ADD_WEIGHTEDY( 0, 1, 0); ADD_WEIGHTEDY( 1, 1, 0);
+
+ ADD_WEIGHTEDY(-1,-1, 1); ADD_WEIGHTEDY( 0,-1, 1); ADD_WEIGHTEDY( 1,-1, 1);
+ ADD_WEIGHTEDY(-1, 0, 1); ADD_WEIGHTEDY( 0, 0, 1); ADD_WEIGHTEDY( 1, 0, 1);
+ ADD_WEIGHTEDY(-1, 1, 1); ADD_WEIGHTEDY( 0, 1, 1); ADD_WEIGHTEDY( 1, 1, 1);
+ final[1] = result;
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // z derivative
+ ///////////////////////////////////////////////////////////////////////////////////////
+ result = 0.0f;
+ ADD_WEIGHTEDZ(-1,-1, -1); ADD_WEIGHTEDZ( 0,-1, -1); ADD_WEIGHTEDZ( 1,-1, -1);
+ ADD_WEIGHTEDZ(-1, 0, -1); ADD_WEIGHTEDZ( 0, 0, -1); ADD_WEIGHTEDZ( 1, 0, -1);
+ ADD_WEIGHTEDZ(-1, 1, -1); ADD_WEIGHTEDZ( 0, 1, -1); ADD_WEIGHTEDZ( 1, 1, -1);
+
+ ADD_WEIGHTEDZ(-1,-1, 0); ADD_WEIGHTEDZ( 0,-1, 0); ADD_WEIGHTEDZ( 1,-1, 0);
+ ADD_WEIGHTEDZ(-1, 0, 0); ADD_WEIGHTEDZ( 0, 0, 0); ADD_WEIGHTEDZ( 1, 0, 0);
+ ADD_WEIGHTEDZ(-1, 1, 0); ADD_WEIGHTEDZ( 0, 1, 0); ADD_WEIGHTEDZ( 1, 1, 0);
+
+ ADD_WEIGHTEDZ(-1,-1, 1); ADD_WEIGHTEDZ( 0,-1, 1); ADD_WEIGHTEDZ( 1,-1, 1);
+ ADD_WEIGHTEDZ(-1, 0, 1); ADD_WEIGHTEDZ( 0, 0, 1); ADD_WEIGHTEDZ( 1, 0, 1);
+ ADD_WEIGHTEDZ(-1, 1, 1); ADD_WEIGHTEDZ( 0, 1, 1); ADD_WEIGHTEDZ( 1, 1, 1);
+ final[2] = result;
+
+ //debMsg("FINAL","at "<<p<<" = "<<final); // DEBUG
+ return final;
+}
+#undef ADD_WEIGHTEDX
+#undef ADD_WEIGHTEDY
+#undef ADD_WEIGHTEDZ
+
+inline Real WaveletNoiseField::evaluate(Vec3 pos, int tile) {
+ pos[0] *= mGsInvX;
+ pos[1] *= mGsInvY;
+ pos[2] *= mGsInvZ;
+ pos += mSeedOffset;
+
+ // time anim
+ pos += Vec3(getTime());
+
+ pos[0] *= mPosScale[0];
+ pos[1] *= mPosScale[1];
+ pos[2] *= mPosScale[2];
+ pos += mPosOffset;
+
+ const int n3 = square(NOISE_TILE_SIZE) * NOISE_TILE_SIZE;
+ Real v = WNoise(pos, &mNoiseTile[tile*n3]);
+
+ v += mValOffset;
+ v *= mValScale;
+ if (mClamp) {
+ if (v< mClampNeg) v = mClampNeg;
+ if (v> mClampPos) v = mClampPos;
+ }
+ return v;
+}
+
+inline Vec3 WaveletNoiseField::evaluateVec(Vec3 pos, int tile) {
+ pos[0] *= mGsInvX;
+ pos[1] *= mGsInvY;
+ pos[2] *= mGsInvZ;
+ pos += mSeedOffset;
+
+ // time anim
+ pos += Vec3(getTime());
+
+ pos[0] *= mPosScale[0];
+ pos[1] *= mPosScale[1];
+ pos[2] *= mPosScale[2];
+ pos += mPosOffset;
+
+ const int n3 = square(NOISE_TILE_SIZE) * NOISE_TILE_SIZE;
+ Vec3 v = WNoiseVec(pos, &mNoiseTile[tile*n3]);
+
+ v += Vec3(mValOffset);
+ v *= mValScale;
+
+ if (mClamp) {
+ for(int i=0; i<3; i++) {
+ if (v[i]< mClampNeg) v[i] = mClampNeg;
+ if (v[i]> mClampPos) v[i] = mClampPos;
+ }
+ }
+ return v;
+}
+
+inline Vec3 WaveletNoiseField::evaluateCurl(Vec3 pos) {
+ // gradients of w0-w2
+ Vec3 d0 = evaluateVec(pos,0),
+ d1 = evaluateVec(pos,1),
+ d2 = evaluateVec(pos,2);
+
+ return Vec3(d0.y-d1.z, d2.z-d0.x, d1.x-d2.y);
+}
+
+} // namespace
+
+#endif
diff --git a/source/blender/python/manta_full/source/particle.cpp b/source/blender/python/manta_full/source/particle.cpp new file mode 100644 index 00000000000..ec530b1b7fb --- /dev/null +++ b/source/blender/python/manta_full/source/particle.cpp @@ -0,0 +1,358 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2013 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Particle data functionality + * + ******************************************************************************/ + +#include <fstream> +#if NO_ZLIB!=1 +#include <zlib.h> +#endif +#include "particle.h" +#include "levelset.h" +#include "fileio.h" +#include <cstring> + +using namespace std; +namespace Manta { + + +ParticleBase::ParticleBase(FluidSolver* parent) : + PbClass(parent), mAllowCompress(true), mFreePdata(false) { +} + +ParticleBase::~ParticleBase() +{ + // make sure data fields now parent system is deleted + for(int i=0; i<(int)mPartData.size(); ++i) + mPartData[i]->setParticleSys(NULL); + + if(mFreePdata) { + for(int i=0; i<(int)mPartData.size(); ++i) + delete mPartData[i]; + } + +} + +std::string ParticleBase::infoString() const { + return "ParticleSystem " + mName + " <no info>"; +} + +void ParticleBase::cloneParticleData(ParticleBase* nm) { + // clone additional data , and make sure the copied particle system deletes it + nm->mFreePdata = true; + for(int i=0; i<(int)mPartData.size(); ++i) { + ParticleDataBase* pdata = mPartData[i]->clone(); + nm->registerPdata(pdata); + } +} + +void ParticleBase::deregister(ParticleDataBase* pdata) { + bool done = false; + // remove pointer from particle data list + for(int i=0; i<(int)mPartData.size(); ++i) { + if(mPartData[i] == pdata) { + if(i<(int)mPartData.size()-1) + mPartData[i] = mPartData[mPartData.size()-1]; + mPartData.pop_back(); + done = true; + } + } + if(!done) + errMsg("Invalid pointer given, not registered!"); +} + +PbClass* ParticleBase::create(PbType t, PbTypeVec T, const string& name) { + _args.add("nocheck",true); + if (t.str() == "") + errMsg("Specify particle data type to create"); + //debMsg( "Pdata creating '"<< t.str , 5 ); + + PbClass* pyObj = PbClass::createPyObject(t.str() + T.str(), name, _args, this->getParent() ); + + ParticleDataBase* pdata = dynamic_cast<ParticleDataBase*>(pyObj); + if(!pdata) { + errMsg("Unable to get particle data pointer from newly created object. Only create ParticleData type with a ParticleSys.creat() call, eg, PdataReal, PdataVec3 etc."); + delete pyObj; + return NULL; + } else { + this->registerPdata(pdata); + } + + return pyObj; +} + +void ParticleBase::registerPdata(ParticleDataBase* pdata) { + pdata->setParticleSys(this); + mPartData.push_back(pdata); + + if( pdata->getType() == ParticleDataBase::DATA_REAL ) { + ParticleDataImpl<Real>* pd = dynamic_cast< ParticleDataImpl<Real>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as real!"); + this->registerPdataReal(pd); + } + else if( pdata->getType() == ParticleDataBase::DATA_VEC3 ) { + ParticleDataImpl<Vec3>* pd = dynamic_cast< ParticleDataImpl<Vec3>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as vec3!"); + this->registerPdataVec3(pd); + } + else if( pdata->getType() == ParticleDataBase::DATA_INT ) { + ParticleDataImpl<int>* pd = dynamic_cast< ParticleDataImpl<int>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as int!"); + this->registerPdataInt(pd); + } +} +void ParticleBase::registerPdataReal(ParticleDataImpl<Real>* pd) { mPdataReal.push_back(pd); } +void ParticleBase::registerPdataVec3(ParticleDataImpl<Vec3>* pd) { mPdataVec3.push_back(pd); } +void ParticleBase::registerPdataInt (ParticleDataImpl<int >* pd) { mPdataInt .push_back(pd); } + +void ParticleBase::addAllPdata() { + for(int i=0; i<(int)mPartData.size(); ++i) { + mPartData[i]->add(); + } +} + +std::string ParticleBase::debugInfoPdata() +{ + std::ostringstream sstr; + sstr << "Particle system "<<mName<<" , size: "<< this->getSizeSlow() <<", data "; + for(int i=0; i<(int)mPartData.size(); ++i) { + sstr << i<<":" << mPartData[i]->size() <<" "; + } + sstr << "."; + return sstr.str(); +} + + +BasicParticleSystem::BasicParticleSystem(FluidSolver* parent) + : ParticleSystem<BasicParticleData>(parent) { + this->mAllowCompress = false; +} + +// file io + +void BasicParticleSystem::writeParticlesText(string name) { + ofstream ofs(name.c_str()); + if (!ofs.good()) + errMsg("can't open file!"); + ofs << this->size()<<", pdata: "<< mPartData.size()<<" ("<<mPdataInt.size()<<","<<mPdataReal.size()<<","<<mPdataVec3.size()<<") \n"; + for(int i=0; i<this->size(); ++i) { + ofs << i<<": "<< this->getPos(i) <<" , "<< this->getStatus(i) <<". "; + for(int pd=0; pd<(int)mPdataInt.size() ; ++pd) ofs << mPdataInt [pd]->get(i)<<" "; + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) ofs << mPdataReal[pd]->get(i)<<" "; + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) ofs << mPdataVec3[pd]->get(i)<<" "; + ofs << "\n"; + } + ofs.close(); +} + +void BasicParticleSystem::writeParticlesRawPositionsGz(string name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); + if (!gzf) errMsg("can't open file "<<name); + for(int i=0; i<this->size(); ++i) { + Vector3D<float> p = toVec3f( this->getPos(i) ); + gzwrite(gzf, &p, sizeof(float)*3); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +void BasicParticleSystem::writeParticlesRawVelocityGz(string name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); + if (!gzf) errMsg("can't open file "<<name); + if( mPdataVec3.size() < 1 ) errMsg("no vec3 particle data channel found!"); + // note , assuming particle data vec3 0 is velocity! make optional... + for(int i=0; i<this->size(); ++i) { + Vector3D<float> p = toVec3f( mPdataVec3[0]->get(i) ); + gzwrite(gzf, &p, sizeof(float)*3); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + + +void BasicParticleSystem::load(string name ) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if ( ext == ".uni") + readParticlesUni<BasicParticleData>(name, this ); + else + errMsg("particle '" + name +"' filetype not supported for loading"); +} + +void BasicParticleSystem::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".txt") + this->writeParticlesText(name); + else if (ext == ".uni") + writeParticlesUni<BasicParticleData>(name, this); + // raw data formats, very basic for simple data transfer to other programs + else if (ext == ".posgz") + this->writeParticlesRawPositionsGz(name); + else if (ext == ".velgz") + this->writeParticlesRawVelocityGz(name); + else + errMsg("particle '" + name +"' filetype not supported for saving"); +} + +// particle data + +ParticleDataBase::ParticleDataBase(FluidSolver* parent) : + PbClass(parent) , mpParticleSys(NULL) { +} + +ParticleDataBase::~ParticleDataBase() +{ + // notify parent of deletion + if(mpParticleSys) + mpParticleSys->deregister(this); +} + + +// actual data implementation + +template<class T> +ParticleDataImpl<T>::ParticleDataImpl(FluidSolver* parent) : + ParticleDataBase(parent) , mpGridSource(NULL), mGridSourceMAC(false) { +} + +template<class T> +ParticleDataImpl<T>::ParticleDataImpl(FluidSolver* parent, ParticleDataImpl<T>* other) : + ParticleDataBase(parent) , mpGridSource(NULL), mGridSourceMAC(false) { + this->mData = other->mData; +} + +template<class T> +ParticleDataImpl<T>::~ParticleDataImpl() { +} + +template<class T> +int ParticleDataImpl<T>::size() const { + return mData.size(); +} +template<class T> +void ParticleDataImpl<T>::add() { + // add zero'ed entry + T tmp = T(0.); + // for debugging, force init: + //tmp = T(0.02 * mData.size()); // increasing + //tmp = T(1.); // constant 1 + return mData.push_back(tmp); +} +template<class T> +void ParticleDataImpl<T>::resize(int s) { + mData.resize(s); +} +template<class T> +void ParticleDataImpl<T>::copyValueSlow(int from, int to) { + this->copyValue(from,to); +} +template<class T> +ParticleDataBase* ParticleDataImpl<T>::clone() { + ParticleDataImpl<T>* npd = new ParticleDataImpl<T>( getParent(), this ); + return npd; +} + +template<class T> +void ParticleDataImpl<T>::setSource(Grid<T>* grid, bool isMAC ) { + mpGridSource = grid; + mGridSourceMAC = isMAC; + if(isMAC) assertMsg( dynamic_cast<MACGrid*>(grid) != NULL , "Given grid is not a valid MAC grid"); +} + +template<class T> +void ParticleDataImpl<T>::initNewValue(int idx, Vec3 pos) { + if(!mpGridSource) + mData[idx] = 0; + else { + mData[idx] = mpGridSource->getInterpolated(pos); + } +} +// special handling needed for velocities +template<> +void ParticleDataImpl<Vec3>::initNewValue(int idx, Vec3 pos) { + if(!mpGridSource) + mData[idx] = 0; + else { + if(!mGridSourceMAC) + mData[idx] = mpGridSource->getInterpolated(pos); + else + mData[idx] = ((MACGrid*)mpGridSource)->getInterpolated(pos); + } +} + +template<typename T> +void ParticleDataImpl<T>::load(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if ( ext == ".uni") + readPdataUni<T>(name, this); + else + errMsg("particle data '" + name +"' filetype not supported for loading"); +} + +template<typename T> +void ParticleDataImpl<T>::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".uni") + writePdataUni<T>(name, this); + else + errMsg("particle data '" + name +"' filetype not supported for saving"); +} + +// specializations + +template<> +ParticleDataBase::PdataType ParticleDataImpl<int>::getType() const { + return ParticleDataBase::DATA_INT; +} +template<> +ParticleDataBase::PdataType ParticleDataImpl<Real>::getType() const { + return ParticleDataBase::DATA_REAL; +} +template<> +ParticleDataBase::PdataType ParticleDataImpl<Vec3>::getType() const { + return ParticleDataBase::DATA_VEC3; +} + +// note, we need a flag value for functions such as advection +// ideally, this value should never be modified +int ParticleIndexData::flag = 0; +Vec3 ParticleIndexData::pos = Vec3(0.,0.,0.); + +// explicit instantiation +template class ParticleDataImpl<int>; +template class ParticleDataImpl<Real>; +template class ParticleDataImpl<Vec3>; + +KERNEL(pts) template<class T> +void knSetPdataConst(ParticleDataImpl<T>& pdata, T value) { + pdata[idx] = value; +} +PYTHON void setConstPdata (ParticleDataImpl<Real>& pd, Real value=0.) { knSetPdataConst<Real>(pd,value); } +PYTHON void setConstPdataVec3(ParticleDataImpl<Vec3>& pd, Vec3 value=0.) { knSetPdataConst<Vec3>(pd,value); } +PYTHON void setConstPdataInt (ParticleDataImpl<int >& pd, int value=0.) { knSetPdataConst<int> (pd,value); } + + + +} // namespace + diff --git a/source/blender/python/manta_full/source/particle.h b/source/blender/python/manta_full/source/particle.h new file mode 100644 index 00000000000..28528d15e97 --- /dev/null +++ b/source/blender/python/manta_full/source/particle.h @@ -0,0 +1,608 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for particle systems + * + ******************************************************************************/ + +#ifndef _PARTICLE_H +#define _PARTICLE_H + +#include <vector> +#include "grid.h" +#include "vectorbase.h" +#include "integrator.h" +#include "randomstream.h" +namespace Manta { + +// fwd decl +template<class T> class Grid; +class ParticleDataBase; +template<class T> class ParticleDataImpl; + +//! Baseclass for particle systems. Does not implement any data +PYTHON class ParticleBase : public PbClass { +public: + enum SystemType { BASE=0, PARTICLE, VORTEX, FILAMENT, FLIP, TURBULENCE, INDEX }; + + enum ParticleStatus { + PNONE = 0, + PNEW = (1<<1), // particles newly created in this step + PDELETE = (1<<10), // mark as deleted, will be deleted in next compress() step + PINVALID = (1<<30), // unused + }; + + PYTHON ParticleBase(FluidSolver* parent); + virtual ~ParticleBase(); + + //! copy all the particle data thats registered with the other particle system to this one + virtual void cloneParticleData(ParticleBase* nm); + + virtual SystemType getType() const { return BASE; } + virtual std::string infoString() const; + virtual ParticleBase* clone() { assertMsg( false , "Dont use, override..."); return NULL; } + + // slow virtual function to query size, do not use in kernels! use size() instead + virtual int getSizeSlow() const { assertMsg( false , "Dont use, override..."); return 0; } + + //! add a position as potential candidate for new particle (todo, make usable from parallel threads) + inline void addBuffered(const Vec3& pos); + + //! debug info about pdata + std::string debugInfoPdata(); + + // particle data functions + + //! create a particle data object + PYTHON PbClass* create(PbType type, PbTypeVec T=PbTypeVec(), const std::string& name = ""); + //! add a particle data field, set its parent particle-system pointer + void registerPdata(ParticleDataBase* pdata); + void registerPdataReal(ParticleDataImpl<Real>* pdata); + void registerPdataVec3(ParticleDataImpl<Vec3>* pdata); + void registerPdataInt (ParticleDataImpl<int >* pdata); + //! remove a particle data entry + void deregister(ParticleDataBase* pdata); + //! add one zero entry to all data fields + void addAllPdata(); + // note - deletion of pdata is handled in compress function + + //! how many are there? + int getNumPdata() const { return mPartData.size(); } + //! access one of the fields + ParticleDataBase* getPdata(int i) { return mPartData[i]; } + +protected: + //! new particle candidates + std::vector<Vec3> mNewBuffer; + + //! allow automatic compression / resize? disallowed for, eg, flip particle systems + bool mAllowCompress; + + //! store particle data , each pointer has its own storage vector of a certain type (int, real, vec3) + std::vector<ParticleDataBase*> mPartData; + //! lists of different types, for fast operations w/o virtual function calls (all calls necessary per particle) + std::vector< ParticleDataImpl<Real> *> mPdataReal; + std::vector< ParticleDataImpl<Vec3> *> mPdataVec3; + std::vector< ParticleDataImpl<int> *> mPdataInt; + //! indicate that pdata of this particle system is copied, and needs to be freed + bool mFreePdata; +}; + + +//! Main class for particle systems +/*! Basetype S must at least contain flag, pos fields */ +PYTHON template<class S> class ParticleSystem : public ParticleBase { +public: + PYTHON ParticleSystem(FluidSolver* parent) : ParticleBase(parent), mDeletes(0), mDeleteChunk(0) {} + virtual ~ParticleSystem() {}; + + virtual SystemType getType() const { return S::getType(); }; + + // accessors + inline S& operator[](int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const S& operator[](int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + // return size of container + PYTHON inline int size() const { return mData.size(); } + // slow virtual function of base class, also returns size + virtual int getSizeSlow() const { return size(); } + + // query status + inline int getStatus(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx].flag; } + inline bool isActive(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PDELETE) == 0; } + + //! safe accessor for python + PYTHON void setPos(int idx, const Vec3& pos) { DEBUG_ONLY(checkPartIndex(idx)); mData[idx].pos = pos; } + PYTHON Vec3 getPos(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx].pos; } + //! copy all positions into pdata vec3 field + PYTHON void getPosPdata(ParticleDataImpl<Vec3>& target); + PYTHON void setPosPdata(ParticleDataImpl<Vec3>& source); + //! transform coordinate system from one grid size to another (usually upon load) + void transformPositions( Vec3i dimOld, Vec3i dimNew ); + + //! explicitly trigger compression from outside + void doCompress() { if ( mDeletes > mDeleteChunk) compress(); } + //! insert buffered positions as new particles, update additional particle data + void insertBufferedParticles(); + //! resize data vector, and all pdata fields + void resizeAll(int newsize); + + // adding and deleting + inline void kill(int idx); + int add(const S& data); + // remove all particles, init 0 length arrays (also pdata) + PYTHON void clear(); + + //! Advect particle in grid velocity field + PYTHON void advectInGrid(FlagGrid& flags, MACGrid& vel, int integrationMode, bool deleteInObstacle=true ); + + //! Project particles outside obstacles + PYTHON void projectOutside(Grid<Vec3>& gradient); + + virtual ParticleBase* clone(); + virtual std::string infoString() const; + + //! debugging + inline void checkPartIndex(int idx) const; + +protected: + //! deletion count , and interval for re-compressing + int mDeletes, mDeleteChunk; + //! the particle data + std::vector<S> mData; + + //! reduce storage , called by doCompress + virtual void compress(); +}; + +//****************************************************************************** + +//! Simplest data class for particle systems +struct BasicParticleData { +public: + BasicParticleData() : pos(0.), flag(0) {} + BasicParticleData(const Vec3& p) : pos(p), flag(0) {} + static ParticleBase::SystemType getType() { return ParticleBase::PARTICLE; } + + //! data + Vec3 pos; + int flag; +}; + +PYTHON class BasicParticleSystem : public ParticleSystem<BasicParticleData> { +public: + PYTHON BasicParticleSystem(FluidSolver* parent); + + //! file io + PYTHON void save(std::string name); + PYTHON void load(std::string name); + + // save to text file + void writeParticlesText(std::string name); + // other output formats + void writeParticlesRawPositionsGz(std::string name); + void writeParticlesRawVelocityGz(std::string name); + + // add particles in python + PYTHON void addParticle(Vec3 pos) { add(BasicParticleData(pos)); } + + // dangerous, get low level access - avoid usage, only used in vortex filament advection for now + std::vector<BasicParticleData>& getData() { return mData; } +}; + + +//****************************************************************************** + +//! Index into other particle system +// used for grid based neighborhood searches on generic particle systems (stores +// only active particles, and reduces copied data) +// note - pos & flag are disabled here, do not use! +struct ParticleIndexData { +public: + ParticleIndexData() : sourceIndex(0) {} + static ParticleBase::SystemType getType() { return ParticleBase::INDEX; } + + int sourceIndex; // index of this particle in the original particle system + // note - the following two are needed for template instantiation, but not used + // for the particle index system (use values from original one!) + static Vec3 pos; // do not use... + static int flag; // not needed usally + //Vec3 pos; // enable for debugging +}; + +PYTHON class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> { +public: + PYTHON ParticleIndexSystem(FluidSolver* parent) : ParticleSystem<ParticleIndexData>(parent) {}; + + //! we only need a resize function... + void resize(int size) { mData.resize(size); } +}; + + + +//****************************************************************************** + +//! Particle set with connectivity +PYTHON template<class DATA, class CON> +class ConnectedParticleSystem : public ParticleSystem<DATA> { +public: + PYTHON ConnectedParticleSystem(FluidSolver* parent) : ParticleSystem<DATA>(parent) {} + + // accessors + inline bool isSegActive(int i) { return (mSegments[i].flag & ParticleBase::PDELETE) == 0; } + inline int segSize() const { return mSegments.size(); } + inline CON& seg(int i) { return mSegments[i]; } + inline const CON& seg(int i) const { return mSegments[i]; } + + virtual ParticleBase* clone(); + +protected: + std::vector<CON> mSegments; + virtual void compress(); +}; + +//****************************************************************************** + +//! abstract interface for particle data +PYTHON class ParticleDataBase : public PbClass { +public: + PYTHON ParticleDataBase(FluidSolver* parent); + virtual ~ParticleDataBase(); + + enum PdataType { UNKNOWN=0, DATA_INT, DATA_REAL, DATA_VEC3 }; + + // interface functions, using assert instead of pure virtual for python compatibility + virtual int size() const { assertMsg( false , "Dont use, override..."); return 0; } + virtual void add() { assertMsg( false , "Dont use, override..."); return; } + virtual ParticleDataBase* clone() { assertMsg( false , "Dont use, override..."); return NULL; } + virtual PdataType getType() const { assertMsg( false , "Dont use, override..."); return UNKNOWN; } + virtual void resize(int size) { assertMsg( false , "Dont use, override..."); return; } + virtual void copyValueSlow(int from, int to) { assertMsg( false , "Dont use, override..."); return; } + + //! set base pointer + void setParticleSys(ParticleBase* set) { mpParticleSys = set; } + + //! debugging + inline void checkPartIndex(int idx) const; + +protected: + ParticleBase* mpParticleSys; +}; + + +//! abstract interface for particle data +PYTHON template<class T> +class ParticleDataImpl : public ParticleDataBase { +public: + PYTHON ParticleDataImpl(FluidSolver* parent); + ParticleDataImpl(FluidSolver* parent, ParticleDataImpl<T>* other); + virtual ~ParticleDataImpl(); + + //! access data + inline T& get(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const T get(int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline T& operator[](int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const T operator[](int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + + // set all values to 0, note - different from particleSystem::clear! doesnt modify size of array (has to stay in sync with parent system) + PYTHON void clear(); + + //! set grid from which to get data... + PYTHON void setSource(Grid<T>* grid, bool isMAC=false ); + + // particle data base interface + virtual int size() const; + virtual void add(); + virtual ParticleDataBase* clone(); + virtual PdataType getType() const; + virtual void resize(int s); + virtual void copyValueSlow(int from, int to); + + // fast inlined functions for per particle operations + inline void copyValue(int from, int to) { get(to) = get(from); } + void initNewValue(int idx, Vec3 pos); + + //! file io + PYTHON void save(std::string name); + PYTHON void load(std::string name); +protected: + //! data storage + std::vector<T> mData; + + //! optionally , we might have an associated grid from which to grab new data + Grid<T>* mpGridSource; + //! unfortunately , we need to distinguish mac vs regular vec3 + bool mGridSourceMAC; +}; + +PYTHON alias ParticleDataImpl<int> PdataInt; +PYTHON alias ParticleDataImpl<Real> PdataReal; +PYTHON alias ParticleDataImpl<Vec3> PdataVec3; + + +//****************************************************************************** +// Implementation +//****************************************************************************** + +const int DELETE_PART = 20; // chunk size for compression + +void ParticleBase::addBuffered(const Vec3& pos) { + mNewBuffer.push_back(pos); +} + +template<class S> +void ParticleSystem<S>::clear() { + mDeleteChunk = mDeletes = 0; + this->resizeAll(0); // instead of mData.clear +} + +template<class S> +int ParticleSystem<S>::add(const S& data) { + mData.push_back(data); + mDeleteChunk = mData.size() / DELETE_PART; + this->addAllPdata(); + return mData.size()-1; +} + +template<class S> +inline void ParticleSystem<S>::kill(int idx) { + assertMsg(idx>=0 && idx<size(), "Index out of bounds"); + mData[idx].flag |= PDELETE; + if ( (++mDeletes > mDeleteChunk) && (mAllowCompress) ) compress(); +} + +template<class S> +void ParticleSystem<S>::getPosPdata(ParticleDataImpl<Vec3>& target) { + for(int i=0; i<(int)this->size(); ++i) { + target[i] = this->getPos(i); + } +} +template<class S> +void ParticleSystem<S>::setPosPdata(ParticleDataImpl<Vec3>& target) { + for(int i=0; i<(int)this->size(); ++i) { + this->getPos(i) = target[i]; + } +} + +template<class S> +void ParticleSystem<S>::transformPositions( Vec3i dimOld, Vec3i dimNew ) +{ + Vec3 factor = calcGridSizeFactor( dimNew, dimOld ); + for(int i=0; i<(int)this->size(); ++i) { + this->setPos(i, this->getPos(i) * factor ); + } +} + +// check for deletion/invalid position, otherwise return velocity +KERNEL(pts) template<class S> returns(std::vector<Vec3> u(size)) +std::vector<Vec3> GridAdvectKernel (std::vector<S>& p, const MACGrid& vel, const FlagGrid& flags, Real dt, + bool deleteInObstacle ) +{ + if (p[idx].flag & ParticleBase::PDELETE) { + u[idx] =_0; + } else if (!flags.isInBounds(p[idx].pos,1) || flags.isObstacle(p[idx].pos)) { + u[idx] = _0; + + // for simple tracer particles, its convenient to delete particles right away + // for other sim types, eg flip, we can try to fix positions later on + if(deleteInObstacle) + p[idx].flag |= ParticleBase::PDELETE; + } else { + u[idx] = vel.getInterpolated(p[idx].pos) * dt; + } +}; + +// final check after advection to make sure particles haven't escaped +// (similar to particle advection kernel) +KERNEL(pts) template<class S> +void KnDeleteInObstacle(std::vector<S>& p, const FlagGrid& flags) { + if (p[idx].flag & ParticleBase::PDELETE) return; + if (!flags.isInBounds(p[idx].pos,1) || flags.isObstacle(p[idx].pos)) { + p[idx].flag |= ParticleBase::PDELETE; + } +} +// at least make sure all particles are inside domain +KERNEL(pts) template<class S> +void KnClampPositions(std::vector<S>& p, const FlagGrid& flags) { + if (p[idx].flag & ParticleBase::PDELETE) return; + if (!flags.isInBounds(p[idx].pos,0) ) { + p[idx].pos = clamp( p[idx].pos, Vec3(0.), toVec3(flags.getSize())-Vec3(1.) ); + } +} + +// advection plugin +template<class S> +void ParticleSystem<S>::advectInGrid(FlagGrid& flags, MACGrid& vel, int integrationMode, bool deleteInObstacle ) { + GridAdvectKernel<S> kernel(mData, vel, flags, getParent()->getDt(), deleteInObstacle ); + integratePointSet(kernel, integrationMode); + if(deleteInObstacle) KnDeleteInObstacle<S>( mData, flags); + else KnClampPositions<S> ( mData, flags); +} + +KERNEL(pts, single) // no thread-safe random gen yet +template<class S> +void KnProjectParticles(ParticleSystem<S>& part, Grid<Vec3>& gradient) { + static RandomStream rand (3123984); + const double jlen = 0.1; + + if (part.isActive(idx)) { + // project along levelset gradient + Vec3 p = part[idx].pos; + if (gradient.isInBounds(p)) { + Vec3 n = gradient.getInterpolated(p); + Real dist = normalize(n); + Vec3 dx = n * (-dist + jlen * (1 + rand.getReal())); + p += dx; + } + // clamp to outer boundaries (+jitter) + const double jlen = 0.1; + Vec3 jitter = jlen * rand.getVec3(); + part[idx].pos = clamp(p, Vec3(1,1,1)+jitter, toVec3(gradient.getSize()-1)-jitter); + } +} + +template<class S> +void ParticleSystem<S>::projectOutside(Grid<Vec3>& gradient) { + KnProjectParticles<S>(*this, gradient); +} + +template<class S> +void ParticleSystem<S>::resizeAll(int size) { + // resize all buffers to target size in 1 go + mData.resize(size); + for(int i=0; i<(int)mPartData.size(); ++i) + mPartData[i]->resize(size); +} + +template<class S> +void ParticleSystem<S>::compress() { + int nextRead = mData.size(); + for (int i=0; i<(int)mData.size(); i++) { + while ((mData[i].flag & PDELETE) != 0) { + nextRead--; + mData[i] = mData[nextRead]; + // ugly, but prevent virtual function calls here: + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) mPdataReal[pd]->copyValue(nextRead, i); + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) mPdataVec3[pd]->copyValue(nextRead, i); + for(int pd=0; pd<(int)mPdataInt .size(); ++pd) mPdataInt [pd]->copyValue(nextRead, i); + mData[nextRead].flag = PINVALID; + } + } + if(nextRead<(int)mData.size()) debMsg("Deleted "<<((int)mData.size() - nextRead)<<" particles", 1); // debug info + + resizeAll(nextRead); + mDeletes = 0; + mDeleteChunk = mData.size() / DELETE_PART; +} + +//! insert buffered positions as new particles, update additional particle data +template<class S> +void ParticleSystem<S>::insertBufferedParticles() { + if(mNewBuffer.size()==0) return; + int newCnt = mData.size(); + resizeAll(newCnt + mNewBuffer.size()); + + // clear new flag everywhere + for(int i=0; i<(int)mData.size(); ++i) mData[i].flag &= ~PNEW; + + for(int i=0; i<(int)mNewBuffer.size(); ++i) { + // note, other fields are not initialized here... + mData[newCnt].pos = mNewBuffer[i]; + mData[newCnt].flag = PNEW; + // now init pdata fields from associated grids... + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) + mPdataReal[pd]->initNewValue(newCnt, mNewBuffer[i] ); + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) + mPdataVec3[pd]->initNewValue(newCnt, mNewBuffer[i] ); + for(int pd=0; pd<(int)mPdataInt.size(); ++pd) + mPdataInt[pd]->initNewValue(newCnt, mNewBuffer[i] ); + newCnt++; + } + if(mNewBuffer.size()>0) debMsg("Added & initialized "<<(int)mNewBuffer.size()<<" particles", 1); // debug info + mNewBuffer.clear(); +} + + +template<class DATA, class CON> +void ConnectedParticleSystem<DATA,CON>::compress() { + const int sz = ParticleSystem<DATA>::size(); + int *renumber_back = new int[sz]; + int *renumber = new int[sz]; + for (int i=0; i<sz; i++) + renumber[i] = renumber_back[i] = -1; + + // reorder elements + std::vector<DATA>& data = ParticleSystem<DATA>::mData; + int nextRead = sz; + for (int i=0; i<nextRead; i++) { + if ((data[i].flag & ParticleBase::PDELETE) != 0) { + nextRead--; + data[i] = data[nextRead]; + data[nextRead].flag = 0; + renumber_back[i] = nextRead; + } else + renumber_back[i] = i; + } + + // acceleration structure + for (int i=0; i<nextRead; i++) + renumber[renumber_back[i]] = i; + + // rename indices in filaments + for (int i=0; i<(int)mSegments.size(); i++) + mSegments[i].renumber(renumber); + + ParticleSystem<DATA>::mData.resize(nextRead); + ParticleSystem<DATA>::mDeletes = 0; + ParticleSystem<DATA>::mDeleteChunk = ParticleSystem<DATA>::size() / DELETE_PART; + + delete[] renumber; + delete[] renumber_back; +} + +template<class S> +ParticleBase* ParticleSystem<S>::clone() { + ParticleSystem<S>* nm = new ParticleSystem<S>(getParent()); + if(this->mAllowCompress) compress(); + + nm->mData = mData; + nm->setName(getName()); + this->cloneParticleData(nm); + return nm; +} + +template<class DATA,class CON> +ParticleBase* ConnectedParticleSystem<DATA,CON>::clone() { + ConnectedParticleSystem<DATA,CON>* nm = new ConnectedParticleSystem<DATA,CON>(this->getParent()); + if(this->mAllowCompress) compress(); + + nm->mData = this->mData; + nm->mSegments = mSegments; + nm->setName(this->getName()); + this->cloneParticleData(nm); + return nm; +} + +template<class S> +std::string ParticleSystem<S>::infoString() const { + std::stringstream s; + s << "ParticleSys '" << getName() << "' [" << size() << " parts"; + if(this->getNumPdata()>0) s<< " "<< this->getNumPdata()<<" pd"; + s << "]"; + return s.str(); +} + +template<class S> +inline void ParticleSystem<S>::checkPartIndex(int idx) const { + int mySize = this->size(); + if (idx<0 || idx > mySize ) { + errMsg( "ParticleBase " << " size " << mySize << " : index " << idx << " out of bound " ); + } +} + +inline void ParticleDataBase::checkPartIndex(int idx) const { + int mySize = this->size(); + if (idx<0 || idx > mySize ) { + errMsg( "ParticleData " << " size " << mySize << " : index " << idx << " out of bound " ); + } + if ( mpParticleSys && mpParticleSys->getSizeSlow()!=mySize ) { + errMsg( "ParticleData " << " size " << mySize << " does not match parent! (" << mpParticleSys->getSizeSlow() << ") " ); + } +} + +// set contents to zero, as for a grid +template<class T> +void ParticleDataImpl<T>::clear() { + for(int i=0; i<(int)mData.size(); ++i) mData[i] = 0.; +} + + +} // namespace + +#endif + diff --git a/source/blender/python/manta_full/source/plugin/advection.cpp b/source/blender/python/manta_full/source/plugin/advection.cpp new file mode 100644 index 00000000000..a2afaf8125c --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/advection.cpp @@ -0,0 +1,324 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugins for pressure correction: + * - solve_pressure + * + ******************************************************************************/ + +#include "../util/vectorbase.h" +#include "grid.h" +#include "kernel.h" + +using namespace std; + +namespace Manta { + +static inline bool isNotFluid(FlagGrid& flags, int i, int j, int k) +{ + if ( flags.isFluid(i,j,k) ) return false; + if ( flags.isFluid(i-1,j,k) ) return false; + if ( flags.isFluid(i,j-1,k) ) return false; + if ( flags.is3D() ) { + if ( flags.isFluid(i,j,k-1) ) return false; + } + return true; +} + +//! Semi-Lagrange interpolation kernel +KERNEL(bnd=1) template<class T> +void SemiLagrange (FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& src, Real dt, bool isLevelset) +{ + if (flags.isObstacle(i,j,k)) { + dst(i,j,k) = 0; + return; + } + if (!isLevelset && isNotFluid(flags,i,j,k) ) { + dst(i,j,k) = src(i,j,k); + return; + } + + // SL traceback + Vec3 pos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getCentered(i,j,k) * dt; + dst(i,j,k) = src.getInterpolated(pos); +} + +static inline bool isNotFluidMAC(FlagGrid& flags, int i, int j, int k) +{ + if ( flags.isFluid(i,j,k) ) return false; + return true; +} + +//! Semi-Lagrange interpolation kernel for MAC grids +KERNEL(bnd=1) +void SemiLagrangeMAC(FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& src, Real dt) +{ + if (flags.isObstacle(i,j,k)) { + dst(i,j,k) = 0; + return; + } + if ( isNotFluidMAC(flags,i,j,k) ) { + dst(i,j,k) = src(i,j,k); + return; + } + + // get currect velocity at MAC position + // no need to shift xpos etc. as lookup field is also shifted + Vec3 xpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACX(i,j,k) * dt; + Real vx = src.getInterpolatedComponent<0>(xpos); + Vec3 ypos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACY(i,j,k) * dt; + Real vy = src.getInterpolatedComponent<1>(ypos); + Vec3 zpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACZ(i,j,k) * dt; + Real vz = src.getInterpolatedComponent<2>(zpos); + + dst(i,j,k) = Vec3(vx,vy,vz); +} + +//! Kernel: Correct based on forward and backward SL steps (for both centered & mac grids) +KERNEL(idx) template<class T> +void MacCormackCorrect(FlagGrid& flags, Grid<T>& dst, Grid<T>& old, Grid<T>& fwd, Grid<T>& bwd, + Real strength, bool isLevelSet, bool isMAC=false ) +{ + // note, replacement for isNotFluidMAC and isNotFluid + bool skip = false; + + if (!flags.isFluid(idx)) skip = true; + if(!isMAC) { + if( (idx>=flags.getStrideX()) && (!flags.isFluid(idx-flags.getStrideX()) )) skip = true; + if( (idx>=flags.getStrideY()) && (!flags.isFluid(idx-flags.getStrideY()) )) skip = true; + if ( flags.is3D() ) { + if( (idx>=flags.getStrideZ()) &&(!flags.isFluid(idx-flags.getStrideZ()) )) skip = true; + } } + if ( skip ) { + dst[idx] = isLevelSet ? fwd[idx] : (T)0.0; + return; + } + + // note, strenth of correction can be modified here + dst[idx] = fwd[idx] + strength * 0.5 * (old[idx] - bwd[idx]); +} + +// Helper to collect min/max in a template +template<class T> inline void getMinMax(T& minv, T& maxv, const T& val) { + if (val < minv) minv = val; + if (val > maxv) maxv = val; +} +template<> inline void getMinMax<Vec3>(Vec3& minv, Vec3& maxv, const Vec3& val) { + getMinMax(minv.x, maxv.x, val.x); + getMinMax(minv.y, maxv.y, val.y); + getMinMax(minv.z, maxv.z, val.z); +} + + +//! Helper function for clamping non-mac grids +template<class T> +inline T doClampComponent(const Vec3i& upperClamp, Grid<T>& orig, T dst, const Vec3i& posFwd) { + // clamp forward lookup to grid + const int i0 = clamp(posFwd.x, 0, upperClamp.x-1); + const int j0 = clamp(posFwd.y, 0, upperClamp.y-1); + const int k0 = clamp(posFwd.z, 0, (orig.is3D() ? (upperClamp.z-1) : 1) ); + const int i1 = i0+1, j1 = j0+1, k1= (orig.is3D() ? (k0+1) : k0); + + if (!orig.isInBounds(Vec3i(i0,j0,k0),1)) { + return dst; + } + + // find min/max around fwd pos + T minv = orig(i0,j0,k0), maxv = minv; + getMinMax(minv, maxv, orig(i1,j0,k0)); + getMinMax(minv, maxv, orig(i0,j1,k0)); + getMinMax(minv, maxv, orig(i1,j1,k0)); + getMinMax(minv, maxv, orig(i0,j0,k1)); + getMinMax(minv, maxv, orig(i1,j0,k1)); + getMinMax(minv, maxv, orig(i0,j1,k1)); + getMinMax(minv, maxv, orig(i1,j1,k1)); + + // write clamped value + return clamp(dst, minv, maxv); +} + +//! Helper function for clamping MAC grids +template<int c> +inline Real doClampComponentMAC(const Vec3i& upperClamp, MACGrid& orig, Real dst, const Vec3i& posFwd) { + // clamp forward lookup to grid + const int i0 = clamp(posFwd.x, 0, upperClamp.x-1); + const int j0 = clamp(posFwd.y, 0, upperClamp.y-1); + const int k0 = clamp(posFwd.z, 0, (orig.is3D() ? (upperClamp.z-1) : 1) ); + const int i1 = i0+1, j1 = j0+1, k1= (orig.is3D() ? (k0+1) : k0); + if (!orig.isInBounds(Vec3i(i0,j0,k0),1)) + return dst; + + // find min/max around fwd pos + Real minv = orig(i0,j0,k0)[c], maxv = minv; + getMinMax(minv, maxv, orig(i1,j0,k0)[c]); + getMinMax(minv, maxv, orig(i0,j1,k0)[c]); + getMinMax(minv, maxv, orig(i1,j1,k0)[c]); + getMinMax(minv, maxv, orig(i0,j0,k1)[c]); + getMinMax(minv, maxv, orig(i1,j0,k1)[c]); + getMinMax(minv, maxv, orig(i0,j1,k1)[c]); + getMinMax(minv, maxv, orig(i1,j1,k1)[c]); + + return clamp(dst, minv, maxv); +} + +//! Kernel: Clamp obtained value to min/max in source area, and reset values that point out of grid or into boundaries +// (note - MAC grids are handled below) +KERNEL(bnd=1) template<class T> +void MacCormackClamp(FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& orig, Grid<T>& fwd, Real dt) +{ + if (flags.isObstacle(i,j,k)) + return; + if ( isNotFluid(flags,i,j,k) ) { + dst(i,j,k) = fwd(i,j,k); + return; + } + + T dval = dst(i,j,k); + Vec3i upperClamp = flags.getSize() - 1; + + // lookup forward/backward + Vec3i posFwd = toVec3i( Vec3(i,j,k) - vel.getCentered(i,j,k) * dt ); + Vec3i posBwd = toVec3i( Vec3(i,j,k) + vel.getCentered(i,j,k) * dt ); + + dval = doClampComponent<T>(upperClamp, orig, dval, posFwd ); + + // test if lookups point out of grid or into obstacle + if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || + posBwd.x < 0 || posBwd.y < 0 || posBwd.z < 0 || + posFwd.x > upperClamp.x || posFwd.y > upperClamp.y || ((posFwd.z > upperClamp.z)&&flags.is3D()) || + posBwd.x > upperClamp.x || posBwd.y > upperClamp.y || ((posBwd.z > upperClamp.z)&&flags.is3D()) || + flags.isObstacle(posFwd) || flags.isObstacle(posBwd) ) + { + dval = fwd(i,j,k); + } + dst(i,j,k) = dval; +} + +//! Kernel: same as MacCormackClamp above, but specialized version for MAC grids +KERNEL(bnd=1) +void MacCormackClampMAC (FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& orig, MACGrid& fwd, Real dt) +{ + if (flags.isObstacle(i,j,k)) + return; + if ( isNotFluidMAC(flags,i,j,k) ) { + dst(i,j,k) = fwd(i,j,k); + return; + } + + Vec3 pos(i,j,k); + Vec3 dval = dst(i,j,k); + Vec3i upperClamp = flags.getSize() - 1; + + // get total fwd lookup + Vec3i posFwd = toVec3i( Vec3(i,j,k) - vel.getCentered(i,j,k) * dt ); + Vec3i posBwd = toVec3i( Vec3(i,j,k) + vel.getCentered(i,j,k) * dt ); + + // clamp individual components + dval.x = doClampComponentMAC<0>(upperClamp, orig, dval.x, toVec3i( pos - vel.getAtMACX(i,j,k) * dt) ); + dval.y = doClampComponentMAC<1>(upperClamp, orig, dval.y, toVec3i( pos - vel.getAtMACY(i,j,k) * dt) ); + dval.z = doClampComponentMAC<2>(upperClamp, orig, dval.z, toVec3i( pos - vel.getAtMACZ(i,j,k) * dt) ); + + // test if lookups point out of grid or into obstacle + if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || + posBwd.x < 0 || posBwd.y < 0 || posBwd.z < 0 || + posFwd.x > upperClamp.x || posFwd.y > upperClamp.y || ((posFwd.z > upperClamp.z)&&flags.is3D()) || + posBwd.x > upperClamp.x || posBwd.y > upperClamp.y || ((posBwd.z > upperClamp.z)&&flags.is3D()) + //|| flags.isObstacle(posFwd) || flags.isObstacle(posBwd) // note - this unfortunately introduces asymmetry... TODO update + ) + { + dval = fwd(i,j,k); + } + + // writeback + dst(i,j,k) = dval; +} + +//! template function for performing SL advection +template<class GridType> +void fnAdvectSemiLagrange(FluidSolver* parent, FlagGrid& flags, MACGrid& vel, GridType& orig, int order, Real strength) { + typedef typename GridType::BASETYPE T; + + Real dt = parent->getDt(); + bool levelset = orig.getType() & GridBase::TypeLevelset; + + // forward step + GridType fwd(parent); + SemiLagrange<T> (flags, vel, fwd, orig, dt, levelset); + + if (order == 1) { + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + GridType bwd(parent); + GridType newGrid(parent); + + // bwd <- backwards step + SemiLagrange<T> (flags, vel, bwd, fwd, -dt, levelset); + + // newGrid <- compute correction + MacCormackCorrect<T> (flags, newGrid, orig, fwd, bwd, strength, levelset); + + // clamp values + MacCormackClamp<T> (flags, vel, newGrid, orig, fwd, dt); + + orig.swap(newGrid); + } +} + +//! template function for performing SL advection: specialized version for MAC grids +template<> +void fnAdvectSemiLagrange<MACGrid>(FluidSolver* parent, FlagGrid& flags, MACGrid& vel, MACGrid& orig, int order, Real strength) { + Real dt = parent->getDt(); + + // forward step + MACGrid fwd(parent); + SemiLagrangeMAC (flags, vel, fwd, orig, dt); + + if (order == 1) { + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + MACGrid bwd(parent); + MACGrid newGrid(parent); + + // bwd <- backwards step + SemiLagrangeMAC (flags, vel, bwd, fwd, -dt); + + // newGrid <- compute correction + MacCormackCorrect<Vec3> (flags, newGrid, orig, fwd, bwd, strength, false, true); + + // clamp values + MacCormackClampMAC (flags, vel, newGrid, orig, fwd, dt); + + orig.swap(newGrid); + } +} + +//! Perform semi-lagrangian advection of target Real- or Vec3 grid +PYTHON void advectSemiLagrange (FlagGrid* flags, MACGrid* vel, GridBase* grid, + int order = 1, Real strength = 1.0) +{ + assertMsg(order==1 || order==2, "AdvectSemiLagrange: Only order 1 (regular SL) and 2 (MacCormack) supported"); + + // determine type of grid + if (grid->getType() & GridBase::TypeReal) { + fnAdvectSemiLagrange< Grid<Real> >(flags->getParent(), *flags, *vel, *((Grid<Real>*) grid), order, strength); + } + else if (grid->getType() & GridBase::TypeMAC) { + fnAdvectSemiLagrange< MACGrid >(flags->getParent(), *flags, *vel, *((MACGrid*) grid), order, strength); + } + else if (grid->getType() & GridBase::TypeVec3) { + fnAdvectSemiLagrange< Grid<Vec3> >(flags->getParent(), *flags, *vel, *((Grid<Vec3>*) grid), order, strength); + } + else + errMsg("AdvectSemiLagrange: Grid Type is not supported (only Real, Vec3, MAC, Levelset)"); +} + +} // end namespace DDF + diff --git a/source/blender/python/manta_full/source/plugin/extforces.cpp b/source/blender/python/manta_full/source/plugin/extforces.cpp new file mode 100644 index 00000000000..e761e0a6ccf --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/extforces.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Set boundary conditions, gravity + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "commonkernels.h" + +using namespace std; + +namespace Manta { + +//! add Forces between fl/fl and fl/em cells +KERNEL(bnd=1) void KnAddForceField(FlagGrid& flags, MACGrid& vel, Grid<Vec3>& force) { + bool curFluid = flags.isFluid(i,j,k); + bool curEmpty = flags.isEmpty(i,j,k); + if (!curFluid && !curEmpty) return; + + if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x += 0.5*(force(i-1,j,k).x + force(i,j,k).x); + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y += 0.5*(force(i,j-1,k).y + force(i,j,k).y); + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z += 0.5*(force(i,j,k-1).z + force(i,j,k).z); +} + +//! add Forces between fl/fl and fl/em cells +KERNEL(bnd=1) void KnAddForce(FlagGrid& flags, MACGrid& vel, Vec3 force) { + bool curFluid = flags.isFluid(i,j,k); + bool curEmpty = flags.isEmpty(i,j,k); + if (!curFluid && !curEmpty) return; + + if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x += force.x; + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y += force.y; + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z += force.z; +} + +//! add gravity forces to all fluid cells +PYTHON void addGravity(FlagGrid& flags, MACGrid& vel, Vec3 gravity) { + Vec3 f = gravity * flags.getParent()->getDt() / flags.getDx(); + KnAddForce(flags, vel, f); +} + +//! add Buoyancy force based on smoke density +KERNEL(bnd=1) void KnAddBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 strength) { + if (!flags.isFluid(i,j,k)) return; + if (flags.isFluid(i-1,j,k)) + vel(i,j,k).x += (0.5 * strength.x) * (density(i,j,k)+density(i-1,j,k)); + if (flags.isFluid(i,j-1,k)) + vel(i,j,k).y += (0.5 * strength.y) * (density(i,j,k)+density(i,j-1,k)); + if (vel.is3D() && flags.isFluid(i,j,k-1)) + vel(i,j,k).z += (0.5 * strength.z) * (density(i,j,k)+density(i,j,k-1)); +} + +//! add Buoyancy force based on smoke density +PYTHON void addBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 gravity) { + Vec3 f = - gravity * flags.getParent()->getDt() / flags.getParent()->getDx(); + KnAddBuoyancy(flags,density, vel, f); +} + + +//! set no-stick wall boundary condition between ob/fl and ob/ob cells +KERNEL void KnSetWallBcs(FlagGrid& flags, MACGrid& vel) { + bool curFluid = flags.isFluid(i,j,k); + bool curObstacle = flags.isObstacle(i,j,k); + if (!curFluid && !curObstacle) return; + + // we use i>0 instead of bnd=1 to check outer wall + if (i>0 && (flags.isObstacle(i-1,j,k) || (curObstacle && flags.isFluid(i-1,j,k)))) + vel(i,j,k).x = 0; + if (j>0 && (flags.isObstacle(i,j-1,k) || (curObstacle && flags.isFluid(i,j-1,k)))) + vel(i,j,k).y = 0; + if (vel.is2D() || (k>0 && (flags.isObstacle(i,j,k-1) || (curObstacle && flags.isFluid(i,j,k-1))))) + vel(i,j,k).z = 0; + + if (curFluid) { + if ((i>0 && flags.isStick(i-1,j,k)) || (i<flags.getSizeX()-1 && flags.isStick(i+1,j,k))) + vel(i,j,k).y = vel(i,j,k).z = 0; + if ((j>0 && flags.isStick(i,j-1,k)) || (j<flags.getSizeY()-1 && flags.isStick(i,j+1,k))) + vel(i,j,k).x = vel(i,j,k).z = 0; + if (vel.is3D() && ((k>0 && flags.isStick(i,j,k-1)) || (k<flags.getSizeZ()-1 && flags.isStick(i,j,k+1)))) + vel(i,j,k).x = vel(i,j,k).y = 0; + } +} + +//! set no-stick boundary condition on walls +PYTHON void setWallBcs(FlagGrid& flags, MACGrid& vel) { + KnSetWallBcs(flags, vel); +} + +//! Kernel: gradient norm operator +KERNEL(bnd=1) void KnConfForce(Grid<Vec3>& force, const Grid<Real>& grid, const Grid<Vec3>& curl, Real str) { + Vec3 grad = 0.5 * Vec3( grid(i+1,j,k)-grid(i-1,j,k), + grid(i,j+1,k)-grid(i,j-1,k), 0.); + if(grid.is3D()) grad[2]= 0.5*( grid(i,j,k+1)-grid(i,j,k-1) ); + normalize(grad); + force(i,j,k) = str * cross(grad, curl(i,j,k)); +} + +PYTHON void vorticityConfinement(MACGrid& vel, FlagGrid& flags, Real strength) { + Grid<Vec3> velCenter(flags.getParent()), curl(flags.getParent()), force(flags.getParent()); + Grid<Real> norm(flags.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GridNorm(norm, curl); + KnConfForce(force, norm, curl, strength); + KnAddForceField(flags, vel, force); +} + +//! enforce a constant inflow/outflow at the grid boundaries +KERNEL void KnSetInflow(MACGrid& vel, int dim, int p0, const Vec3& val) { + Vec3i p(i,j,k); + if (p[dim] == p0 || p[dim] == p0+1) + vel(i,j,k) = val; +} + +//! enforce a constant inflow/outflow at the grid boundaries +PYTHON void setInflowBcs(MACGrid& vel, string dir, Vec3 value) { + for(size_t i=0; i<dir.size(); i++) { + if (dir[i] >= 'x' && dir[i] <= 'z') { + int dim = dir[i]-'x'; + KnSetInflow(vel,dim,0,value); + } else if (dir[i] >= 'X' && dir[i] <= 'Z') { + int dim = dir[i]-'X'; + KnSetInflow(vel,dim,vel.getSize()[dim]-1,value); + } else + errMsg("invalid character in direction string. Only [xyzXYZ] allowed."); + } +} + +} // namespace diff --git a/source/blender/python/manta_full/source/plugin/initplugins.cpp b/source/blender/python/manta_full/source/plugin/initplugins.cpp new file mode 100644 index 00000000000..69579a1327a --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/initplugins.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Tools to setup fields and inflows + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "particle.h" +#include "noisefield.h" +#include "mesh.h" + +using namespace std; + +namespace Manta { + +//! Apply noise to grid +KERNEL +void KnApplyNoise(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Grid<Real>& sdf, Real scale, Real sigma) +{ + if (!flags.isFluid(i,j,k) || sdf(i,j,k) > sigma) return; + Real factor = clamp(1.0-0.5/sigma * (sdf(i,j,k)+sigma), 0.0, 1.0); + + Real target = noise.evaluate(Vec3(i,j,k)) * scale * factor; + if (density(i,j,k) < target) + density(i,j,k) = target; +} + +//! Init noise-modulated density inside shape +PYTHON void densityInflow(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Shape* shape, Real scale=1.0, Real sigma=0) +{ + Grid<Real> sdf = shape->computeLevelset(); + KnApplyNoise(flags, density, noise, sdf, scale, sigma); +} + + +//! Init noise-modulated density inside mesh +PYTHON void densityInflowMesh(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Mesh* mesh, Real scale=1.0, Real sigma=0) +{ + FluidSolver dummy(density.getSize()); + LevelsetGrid sdf(&dummy, false); + mesh->meshSDF(*mesh, sdf, 1.,3); + KnApplyNoise(flags, density, noise, sdf, scale, sigma); +} +//! sample noise field and set pdata with its values (for convenience, scale the noise values) +KERNEL(pts) template<class T> +void knSetPdataNoise(BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale) { + pdata[idx] = noise.evaluate( parts.getPos(idx) ) * scale; +} +KERNEL(pts) template<class T> +void knSetPdataNoiseVec(BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale) { + pdata[idx] = noise.evaluateVec( parts.getPos(idx) ) * scale; +} +PYTHON void setNoisePdata (BasicParticleSystem& parts, ParticleDataImpl<Real>& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoise<Real>(parts, pd,noise,scale); } +PYTHON void setNoisePdataVec3(BasicParticleSystem& parts, ParticleDataImpl<Vec3>& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoiseVec<Vec3>(parts, pd,noise,scale); } +PYTHON void setNoisePdataInt (BasicParticleSystem& parts, ParticleDataImpl<int >& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoise<int> (parts, pd,noise,scale); } + +//! SDF gradient from obstacle flags +PYTHON Grid<Vec3> obstacleGradient(FlagGrid& flags) { + LevelsetGrid levelset(flags.getParent(),false); + Grid<Vec3> gradient(flags.getParent()); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + // build levelset gradient + GradientOp(gradient, levelset); + + FOR_IDX(levelset) { + Vec3 grad = gradient[idx]; + Real s = normalize(grad); + if (s <= 0.1 || levelset[idx] >= 0) + grad=Vec3(0.); + gradient[idx] = grad * levelset[idx]; + } + + return gradient; +} + +PYTHON LevelsetGrid obstacleLevelset(FlagGrid& flags) { + LevelsetGrid levelset(flags.getParent(),false); + Grid<Vec3> gradient(flags.getParent()); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + return levelset; +} + + +} // namespace diff --git a/source/blender/python/manta_full/source/plugin/kepsilon.cpp b/source/blender/python/manta_full/source/plugin/kepsilon.cpp new file mode 100644 index 00000000000..955d05d53d3 --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/kepsilon.cpp @@ -0,0 +1,183 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Turbulence modeling plugins + * + ******************************************************************************/ + +#include "grid.h" +#include "commonkernels.h" +#include "vortexsheet.h" +#include "conjugategrad.h" + +using namespace std; + +namespace Manta { + +// k-epsilon model constants +const Real keCmu = 0.09; +const Real keC1 = 1.44; +const Real keC2 = 1.92; +const Real keS1 = 1.0; +const Real keS2 = 1.3; + +// k-epsilon limiters +const Real keU0 = 1.0; +const Real keImin = 2e-3; +const Real keImax = 1.0; +const Real keNuMin = 1e-3; +const Real keNuMax = 5.0; + +//! clamp k and epsilon to limits +KERNEL(idx) +void KnTurbulenceClamp(Grid<Real>& kgrid, Grid<Real>& egrid, Real minK, Real maxK, Real minNu, Real maxNu) { + Real eps = egrid[idx]; + Real ke = clamp(kgrid[idx],minK,maxK); + Real nu = keCmu*square(ke)/eps; + if (nu > maxNu) + eps = keCmu*square(ke)/maxNu; + if (nu < minNu) + eps = keCmu*square(ke)/minNu; + + kgrid[idx] = ke; + egrid[idx] = eps; +} + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity nu_T=C_mu*k^2/eps +KERNEL(bnd=1) +void KnComputeProduction(const MACGrid& vel, const Grid<Vec3>& velCenter, const Grid<Real>& ke, const Grid<Real>& eps, + Grid<Real>& prod, Grid<Real>& nuT, Grid<Real>* strain, Real pscale = 1.0f) +{ + Real curEps = eps(i,j,k); + if (curEps > 0) { + // turbulent viscosity: nu_T = C_mu * k^2/eps + Real curNu = keCmu * square(ke(i,j,k)) / curEps; + + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, vel(i,j,k+1).z) - vel(i,j,k); + Vec3 ux = 0.5*(velCenter(i+1,j,k)-velCenter(i-1,j,k)); + Vec3 uy = 0.5*(velCenter(i,j+1,k)-velCenter(i,j-1,k)); + Vec3 uz = 0.5*(velCenter(i,j,k+1)-velCenter(i,j,k-1)); + Real S12 = 0.5*(ux.y+uy.x); + Real S13 = 0.5*(ux.z+uz.x); + Real S23 = 0.5*(uy.z+uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + + 2.0*square(S12) + 2.0*square(S13) + 2.0*square(S23); + + // P = 2*nu_T*sum_ij(Sij^2) + prod(i,j,k) = 2.0 * curNu * S2 * pscale; + nuT(i,j,k) = curNu; + if (strain) (*strain)(i,j,k) = sqrt(S2); + } + else { + prod(i,j,k) = 0; + nuT(i,j,k) = 0; + if (strain) (*strain)(i,j,k) = 0; + } +} + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity nu_T=C_mu*k^2/eps +PYTHON void KEpsilonComputeProduction(MACGrid& vel, Grid<Real>& k, Grid<Real>& eps, Grid<Real>& prod, Grid<Real>& nuT, Grid<Real>* strain=0, Real pscale = 1.0f) +{ + // get centered velocity grid + Grid<Vec3> vcenter(k.getParent()); + GetCentered(vcenter, vel); + FillInBoundary(vcenter,1); + + // compute limits + const Real minK = 1.5*square(keU0)*square(keImin); + const Real maxK = 1.5*square(keU0)*square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); + + KnComputeProduction(vel, vcenter, k, eps, prod, nuT, strain, pscale); +} + +//! Integrate source terms of k-epsilon equation +KERNEL(idx) +void KnAddTurbulenceSource(Grid<Real>& kgrid, Grid<Real>& egrid, const Grid<Real>& pgrid, Real dt) { + Real eps = egrid[idx], prod = pgrid[idx], ke = kgrid[idx]; + if (ke <= 0) ke = 1e-3; // pre-clamp to avoid nan + + Real newK = ke + dt*(prod - eps); + Real newEps = eps + dt*(prod * keC1 - eps * keC2) * (eps / ke); + if (newEps <= 0) newEps = 1e-4; // pre-clamp to avoid nan + + kgrid[idx] = newK; + egrid[idx] = newEps; +} + + +//! Integrate source terms of k-epsilon equation +PYTHON void KEpsilonSources(Grid<Real>& k, Grid<Real>& eps, Grid<Real>& prod) { + Real dt = k.getParent()->getDt(); + + KnAddTurbulenceSource(k, eps, prod, dt); + + // compute limits + const Real minK = 1.5*square(keU0)*square(keImin); + const Real maxK = 1.5*square(keU0)*square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); +} + +//! Initialize the domain or boundary conditions +PYTHON void KEpsilonBcs(FlagGrid& flags, Grid<Real>& k, Grid<Real>& eps, Real intensity, Real nu, bool fillArea) { + // compute limits + const Real vk = 1.5*square(keU0)*square(intensity); + const Real ve = keCmu*square(vk) / nu; + + FOR_IDX(k) { + if (fillArea || flags.isObstacle(idx)) { + k[idx] = vk; + eps[idx] = ve; + } + } +} + +//! Gradient diffusion smoothing. Not unconditionally stable -- should probably do substepping etc. +void ApplyGradDiff(const Grid<Real>& grid, Grid<Real>& res, const Grid<Real>& nu, Real dt, Real sigma) { + // should do this (but requires better boundary handling) + /*MACGrid grad(grid.getParent()); + GradientOpMAC(grad, grid); + grad *= nu; + DivergenceOpMAC(res, grad); + res *= dt/sigma; */ + + LaplaceOp(res, grid); + res *= nu; + res *= dt/sigma; +} + +//! Compute k-epsilon turbulent viscosity +PYTHON void KEpsilonGradientDiffusion(Grid<Real>& k, Grid<Real>& eps, Grid<Real>& nuT, Real sigmaU=4.0, MACGrid* vel=0) { + Real dt = k.getParent()->getDt(); + Grid<Real> res(k.getParent()); + + // gradient diffusion of k + ApplyGradDiff(k, res, nuT, dt, keS1); + k += res; + + // gradient diffusion of epsilon + ApplyGradDiff(eps, res, nuT, dt, keS2); + eps += res; + + // gradient diffusion of velocity + if (vel) { + Grid<Real> vc(k.getParent()); + for (int c=0; c<3; c++) { + GetComponent(*vel, vc, c); + ApplyGradDiff(vc, res, nuT, dt, sigmaU); + vc += res; + SetComponent(*vel, vc, c); + } + } +} + + + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/plugin/meshplugins.cpp b/source/blender/python/manta_full/source/plugin/meshplugins.cpp new file mode 100644 index 00000000000..8037447d299 --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/meshplugins.cpp @@ -0,0 +1,623 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Smoothing etc. for meshes + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#include <queue> +#include <algorithm> +#include "mesh.h" +#include "kernel.h" +#include "edgecollapse.h" +#include <mesh.h> +#include <stack> + +using namespace std; + +namespace Manta { + +//! Mesh smoothing +/*! see Desbrun 99 "Implicit fairing of of irregular meshes using diffusion and curvature flow"*/ +PYTHON void smoothMesh(Mesh& mesh, Real strength, int steps = 1, Real minLength=1e-5) { + const Real dt = mesh.getParent()->getDt(); + const Real str = min(dt * strength, (Real)1); + mesh.rebuildQuickCheck(); + + // calculate original mesh volume + Vec3 origCM; + Real origVolume = mesh.computeCenterOfMass(origCM); + + // temp vertices + const int numCorners = mesh.numTris() * 3; + const int numNodes= mesh.numNodes(); + vector<Vec3> temp(numNodes); + vector<bool> visited(numNodes); + + for (int s = 0; s<steps; s++) { + // reset markers + for(size_t i=0; i<visited.size(); i++) visited[i] = false; + + for (int c = 0; c < numCorners; c++) { + const int node = mesh.corners(c).node; + if (visited[node]) continue; + + const Vec3 pos = mesh.nodes(node).pos; + Vec3 dx(_0); + Real totalLen = 0; + + // rotate around vertex + set<int>& ring = mesh.get1Ring(node).nodes; + for(set<int>::iterator it=ring.begin(); it!=ring.end(); it++) { + Vec3 edge = mesh.nodes(*it).pos - pos; + Real len = norm(edge); + + if (len > minLength) { + dx += edge * (_1/len); + totalLen += len; + } else { + totalLen = _0; + break; + } + } + visited[node] = true; + temp[node] = pos; + if (totalLen != 0) + temp[node] += dx * (str / totalLen); + } + + // copy back + for (int n=0; n<numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = temp[n]; + } + + // calculate new mesh volume + Vec3 newCM; + Real newVolume = mesh.computeCenterOfMass(newCM); + + // preserve volume : scale relative to CM + Real beta; +#if defined(WIN32) || defined(_WIN32) + beta = pow( (Real)abs(origVolume/newVolume), (Real)(1./3.) ); +#else + beta = cbrt( origVolume/newVolume ); +# endif + + for (int n=0; n<numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = origCM + (mesh.nodes(n).pos - newCM) * beta; +} + +//! Subdivide and edgecollapse to guarantee mesh with edgelengths between +//! min/maxLength and an angle below minAngle +PYTHON void subdivideMesh(Mesh& mesh, Real minAngle, Real minLength, Real maxLength, bool cutTubes = false) { + // gather some statistics + int edgeSubdivs = 0, edgeCollsAngle = 0, edgeCollsLen = 0, edgeKill = 0; + mesh.rebuildQuickCheck(); + + vector<int> deletedNodes; + map<int,bool> taintedTris; + priority_queue<pair<Real,int> > pq; + + ////////////////////////////////////////// + // EDGE COLLAPSE // + // - particles marked for deletation // + ////////////////////////////////////////// + + for (int t=0; t<mesh.numTris(); t++) { + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // check if at least 2 nodes are marked for delete + bool k[3]; + int numKill = 0; + for (int i=0; i<3; i++) { + k[i] = mesh.nodes(mesh.tris(t).c[i]).flags & Mesh::NfKillme; + if (k[i]) numKill++; + } + if (numKill<2) continue; + + if (k[0] && k[1]) + CollapseEdge(mesh, t, 2, mesh.getEdge(t,0), mesh.getNode(t,0), deletedNodes, taintedTris, edgeKill, cutTubes); + else if (k[1] && k[2]) + CollapseEdge(mesh, t, 0, mesh.getEdge(t,1), mesh.getNode(t,1), deletedNodes, taintedTris, edgeKill, cutTubes); + else if (k[2] && k[0]) + CollapseEdge(mesh, t, 1, mesh.getEdge(t,2), mesh.getNode(t,2), deletedNodes, taintedTris, edgeKill, cutTubes); + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on small triangle angle // + ////////////////////////////////////////// + + if (minAngle > 0) { + for(int t=0; t<mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // if this triangle has already been deleted, ignore it + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // first we find the angles of this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Vec3 ne0 = e0; + Vec3 ne1 = e1; + Vec3 ne2 = e2; + normalize(ne0); + normalize(ne1); + normalize(ne2); + + //Real thisArea = sqrMag(cross(-e2,e0)); + // small angle approximation says sin(x) = arcsin(x) = x, + // arccos(x) = pi/2 - arcsin(x), + // cos(x) = dot(A,B), + // so angle is approximately 1 - dot(A,B). + Real angle[3]; + angle[0] = 1.0-dot(ne0,-ne2); + angle[1] = 1.0-dot(ne1,-ne0); + angle[2] = 1.0-dot(ne2,-ne1); + Real worstAngle = angle[0]; + int which = 0; + if(angle[1]<worstAngle) { + worstAngle = angle[1]; + which = 1; + } + if(angle[2]<worstAngle) { + worstAngle = angle[2]; + which = 2; + } + + // then we see if the angle is too small + if(worstAngle<minAngle) { + Vec3 edgevect; + Vec3 endpoint; + switch(which) { + case 0: + endpoint = mesh.getNode(t,1); + edgevect = e1; + break; + case 1: + endpoint = mesh.getNode(t,2); + edgevect = e2; + break; + case 2: + endpoint = mesh.getNode(t,0); + edgevect = e0; + break; + default: + break; + } + + CollapseEdge(mesh, t,which,edgevect,endpoint,deletedNodes,taintedTris, edgeCollsAngle, cutTubes); + } + } + } + + ////////////////////// + // EDGE SUBDIVISION // + ////////////////////// + + Real maxLength2 = maxLength*maxLength; + for (int t=0; t<mesh.numTris(); t++) { + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Real longest = max(d0,max(d1,d2)); + if(longest > maxLength2) { + pq.push(pair<Real,int>(longest,t)); + } + } + if (maxLength > 0) { + + while(!pq.empty() && pq.top().first>maxLength2) { + // we only want to run through the edge list ONCE + // and we want to subdivide the original edges before we subdivide any newer, shorter edges, + // so whenever we subdivide, we add the 2 new triangles on the end of the SurfaceTri vector + // and mark the original subdivided triangles for deletion. + // when we are done subdividing, we delete the obsolete triangles + + int triA = pq.top().second; + pq.pop(); + + if(taintedTris.find(triA)!=taintedTris.end()) + continue; + + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(triA,0), e1 = mesh.getEdge(triA,1), e2 = mesh.getEdge(triA,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + int which; + if(d0>d1) { + if(d0>d2) { + edgevect = e0; + endpoint = mesh.getNode(triA, 0);; + which = 2; // 2 opposite of edge 0-1 + } else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } else { + if(d1>d2) { + edgevect = e1; + endpoint = mesh.getNode(triA, 1); + which = 0; // 0 opposite of edge 1-2 + } else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + // This edge is too long, so we split it in the middle + + // * + // / \. + // /C0 \. + // / \. + // / \. + // / B \. + // / \. + // /C1 C2 \. + // *---------------* + // \C2 C1 / + // \ / + // \ A / + // \ / + // \ / + // \C0 / + // \ / + // * + // + // BECOMES + // + // * + // /|\. + // / | \. + // /C0|C0\. + // / | \. + // / B1 | B2 \. + // / | \. + // /C1 C2|C1 C2 \. + // *-------*-------* + // \C2 C1|C2 C1/ + // \ | / + // \ A2 | A1 / + // \ | / + // \C0|C0/ + // \ | / + // \|/ + // * + + int triB = -1; bool haveB = false; + Corner ca_old[3],cb_old[3]; + ca_old[0] = mesh.corners(triA, which); + ca_old[1] = mesh.corners(ca_old[0].next); + ca_old[2] = mesh.corners(ca_old[0].prev); + if (ca_old[0].opposite>=0) { + cb_old[0] = mesh.corners(ca_old[0].opposite); + cb_old[1] = mesh.corners(cb_old[0].next); + cb_old[2] = mesh.corners(cb_old[0].prev); + triB = cb_old[0].tri; + haveB = true; + } + //else throw Error("nonmanifold"); + + // subdivide in the middle of the edge and create new triangles + Node newNode; + newNode.flags = 0; + + newNode.pos = endpoint + 0.5*edgevect; // fallback: linear average + // default: use butterfly + if (haveB) + newNode.pos = ModifiedButterflySubdivision(mesh, ca_old[0], cb_old[0], newNode.pos); + + // find indices of two points of 'which'-edge + // merge flags + int P0 = ca_old[1].node; + int P1 = ca_old[2].node; + newNode.flags = mesh.nodes(P0).flags | mesh.nodes(P1).flags; + + Real len0 = norm(mesh.nodes(P0).pos - newNode.pos); + Real len1 = norm(mesh.nodes(P1).pos - newNode.pos); + + // remove P0/P1 1-ring connection + mesh.get1Ring(P0).nodes.erase(P1); + mesh.get1Ring(P1).nodes.erase(P0); + mesh.get1Ring(P0).tris.erase(triA); + mesh.get1Ring(P1).tris.erase(triA); + mesh.get1Ring(ca_old[0].node).tris.erase(triA); + if (haveB) { + mesh.get1Ring(P0).tris.erase(triB); + mesh.get1Ring(P1).tris.erase(triB); + mesh.get1Ring(cb_old[0].node).tris.erase(triB); + } + + // init channel properties for new node + for(int i=0; i<mesh.numNodeChannels(); i++) { + mesh.nodeChannel(i)->addInterpol(P0, P1, len0/(len0+len1)); + } + + // write to array + mesh.addTri(Triangle(ca_old[0].node, ca_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(ca_old[0].node, mesh.numNodes(), ca_old[2].node)); + if (haveB) { + mesh.addTri(Triangle(cb_old[0].node, cb_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(cb_old[0].node, mesh.numNodes(), cb_old[2].node)); + } + mesh.addNode(newNode); + + const int nt = haveB ? 4 : 2; + int triA1 = mesh.numTris()-nt; + int triA2 = mesh.numTris()-nt+1; + int triB1=0, triB2=0; + if (haveB) { + triB1 = mesh.numTris()-nt+2; + triB2 = mesh.numTris()-nt+3; + } + mesh.tris(triA1).flags = mesh.tris(triA).flags; + mesh.tris(triA2).flags = mesh.tris(triA).flags; + mesh.tris(triB1).flags = mesh.tris(triB).flags; + mesh.tris(triB2).flags = mesh.tris(triB).flags; + + // connect new triangles to outside triangles, + // and connect outside triangles to these new ones + for (int c=0; c<3; c++) mesh.addCorner(Corner(triA1,mesh.tris(triA1).c[c])); + for (int c=0; c<3; c++) mesh.addCorner(Corner(triA2,mesh.tris(triA2).c[c])); + if (haveB) { + for (int c=0; c<3; c++) mesh.addCorner(Corner(triB1,mesh.tris(triB1).c[c])); + for (int c=0; c<3; c++) mesh.addCorner(Corner(triB2,mesh.tris(triB2).c[c])); + } + + int baseIdx = 3*(mesh.numTris()-nt); + Corner* cBase = &mesh.corners(baseIdx); + + // set next/prev + for (int t=0; t<nt; t++) + for (int c=0; c<3; c++) { + cBase[t*3+c].next = baseIdx+t*3+((c+1)%3); + cBase[t*3+c].prev = baseIdx+t*3+((c+2)%3); + } + + // set opposites + // A1 + cBase[0].opposite = haveB ? (baseIdx+9) : -1; + cBase[1].opposite = baseIdx+5; + cBase[2].opposite = -1; + if (ca_old[2].opposite>=0) { + cBase[2].opposite = ca_old[2].opposite; + mesh.corners(cBase[2].opposite).opposite = baseIdx+2; + } + // A2 + cBase[3].opposite = haveB ? (baseIdx+6) : -1; + cBase[4].opposite = -1; + if (ca_old[1].opposite>=0) { + cBase[4].opposite = ca_old[1].opposite; + mesh.corners(cBase[4].opposite).opposite = baseIdx+4; + } + cBase[5].opposite = baseIdx+1; + if (haveB) { + // B1 + cBase[6].opposite = baseIdx+3; + cBase[7].opposite = baseIdx+11; + cBase[8].opposite = -1; + if (cb_old[2].opposite>=0) { + cBase[8].opposite = cb_old[2].opposite; + mesh.corners(cBase[8].opposite).opposite = baseIdx+8; + } + // B2 + cBase[9].opposite = baseIdx+0; + cBase[10].opposite = -1; + if (cb_old[1].opposite>=0) { + cBase[10].opposite = cb_old[1].opposite; + mesh.corners(cBase[10].opposite).opposite = baseIdx+10; + } + cBase[11].opposite = baseIdx+7; + } + + //////////////////// + // mark the two original triangles for deletion + taintedTris[triA] = true; + mesh.removeTriFromLookup(triA); + if (haveB) { + taintedTris[triB] = true; + mesh.removeTriFromLookup(triB); + } + + Real areaA1 = mesh.getFaceArea(triA1), areaA2 = mesh.getFaceArea(triA2); + Real areaB1=0, areaB2=0; + if (haveB) { + areaB1 = mesh.getFaceArea(triB1); + areaB2 = mesh.getFaceArea(triB2); + } + + // add channel props for new triangles + for(int i=0; i<mesh.numTriChannels(); i++) { + mesh.triChannel(i)->addSplit(triA, areaA1/(areaA1+areaA2)); + mesh.triChannel(i)->addSplit(triA, areaA2/(areaA1+areaA2)); + if (haveB) { + mesh.triChannel(i)->addSplit(triB, areaB1/(areaB1+areaB2)); + mesh.triChannel(i)->addSplit(triB, areaB2/(areaB1+areaB2)); + } + } + + // add the four new triangles to the prority queue + for(int i=mesh.numTris()-nt; i<mesh.numTris(); i++) { + // find the maximum length edge in this triangle + Vec3 ne0 = mesh.getEdge(i, 0), ne1 = mesh.getEdge(i, 1), ne2 = mesh.getEdge(i, 2); + Real nd0 = normSquare(ne0); + Real nd1 = normSquare(ne1); + Real nd2 = normSquare(ne2); + Real longest = max(nd0,max(nd1,nd2)); + //longest = (int)(longest * 1e2) / 1e2; // HACK: truncate + pq.push(pair<Real,int>(longest,i)); + } + edgeSubdivs++; + } + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on short edge length // + ////////////////////////////////////////// + if (minLength > 0) { + const Real minLength2 = minLength*minLength; + for(int t=0; t<mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // NOTE: + // priority queue does not work so great in the edge collapse case, + // because collapsing one triangle affects the edge lengths + // of many neighbor triangles, + // and we do not update their maximum edge length in the queue. + + // if this triangle has already been deleted, ignore it + //if(taintedTris[t]) + // continue; + + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // first we find the minimum length edge in this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + Real dist2; + int which; + if(d0<d1) { + if(d0<d2) { + dist2 = d0; + edgevect = e0; + endpoint = mesh.getNode(t,0); + which = 2; // 2 opposite of edge 0-1 + } else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t,2); + which = 1; // 1 opposite of edge 2-0 + } + } else { + if(d1<d2) { + dist2 = d1; + edgevect = e1; + endpoint = mesh.getNode(t,1); + which = 0; // 0 opposite of edge 1-2 + } else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t,2); + which = 1; // 1 opposite of edge 2-0 + } + } + // then we see if the min length edge is too short + if(dist2<minLength2) { + CollapseEdge(mesh, t,which,edgevect,endpoint, deletedNodes,taintedTris, edgeCollsLen, cutTubes); + } + } + } + // cleanup nodes and triangles marked for deletion + + // we run backwards through the deleted array, + // replacing triangles with ones from the back + // (this avoids the potential problem of overwriting a triangle + // with a to-be-deleted triangle) + std::map<int,bool>::reverse_iterator tti = taintedTris.rbegin(); + for(;tti!=taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + cout << "Surface subdivision finished with " << mesh.numNodes() << " surface nodes and " << mesh.numTris(); + cout << " surface triangles, edgeSubdivs:" << edgeSubdivs << ", edgeCollapses: " << edgeCollsLen; + cout << " + " << edgeCollsAngle << " + " << edgeKill << endl; + //mesh.sanityCheck(); + +} + +PYTHON void killSmallComponents(Mesh& mesh, int elements = 10) { + const int num = mesh.numTris(); + vector<int> comp(num); + vector<int> numEl; + vector<int> deletedNodes; + vector<bool> isNodeDel(mesh.numNodes()); + map<int,bool> taintedTris; + // enumerate components + int cur=0; + for (int i=0; i<num; i++) { + if (comp[i]==0) { + cur++; + comp[i] = cur; + + stack<int> stack; + stack.push(i); + int cnt = 1; + while(!stack.empty()) { + int tri = stack.top(); + stack.pop(); + for (int c=0; c<3; c++) { + int op = mesh.corners(tri,c).opposite; + if (op < 0) continue; + int ntri = mesh.corners(op).tri; + if (comp[ntri]==0) { + comp[ntri] = cur; + stack.push(ntri); + cnt++; + } + } + } + numEl.push_back(cnt); + } + } + // kill small components + for (int j=0; j<num; j++) { + if (numEl[comp[j]-1] < elements) { + taintedTris[j] = true; + for (int c=0; c<3; c++) { + int n=mesh.tris(j).c[c]; + if (!isNodeDel[n]) { + isNodeDel[n] = true; + deletedNodes.push_back(n); + } + } + } + } + + std::map<int,bool>::reverse_iterator tti = taintedTris.rbegin(); + for(;tti!=taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + + if (!taintedTris.empty()) + cout << "Killed small components : " << deletedNodes.size() << " nodes, " << taintedTris.size() << " tris deleted." << endl; +} + + +} //namespace + diff --git a/source/blender/python/manta_full/source/plugin/pressure.cpp b/source/blender/python/manta_full/source/plugin/pressure.cpp new file mode 100644 index 00000000000..3c826b1d519 --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/pressure.cpp @@ -0,0 +1,310 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * Plugins for pressure correction: solve_pressure, and ghost fluid helpers
+ *
+ ******************************************************************************/
+#include "vectorbase.h"
+#include "kernel.h"
+#include "conjugategrad.h"
+
+using namespace std;
+namespace Manta {
+
+//! Kernel: Construct the right-hand side of the poisson equation
+KERNEL(bnd=1, reduce=+) returns(int cnt=0) returns(double sum=0)
+void MakeRhs (FlagGrid& flags, Grid<Real>& rhs, MACGrid& vel,
+ Grid<Real>* perCellCorr)
+{
+ if (!flags.isFluid(i,j,k)) {
+ rhs(i,j,k) = 0;
+ return;
+ }
+
+ // compute divergence
+ // no flag checks: assumes vel at obstacle interfaces is set to zero
+ Real set = vel(i,j,k).x - vel(i+1,j,k).x +
+ vel(i,j,k).y - vel(i,j+1,k).y;
+ if(vel.is3D()) set+=vel(i,j,k).z - vel(i,j,k+1).z;
+
+ // per cell divergence correction
+ if(perCellCorr)
+ set += perCellCorr->get(i,j,k);
+
+ // obtain sum, cell count
+ sum += set;
+ cnt++;
+
+ rhs(i,j,k) = set;
+}
+
+
+//! Kernel: Apply velocity update from poisson equation
+KERNEL(bnd=1)
+void CorrectVelocity(FlagGrid& flags, MACGrid& vel, Grid<Real>& pressure)
+{
+ int idx = flags.index(i,j,k);
+ if (flags.isFluid(idx))
+ {
+ if (flags.isFluid(i-1,j,k)) vel[idx].x -= (pressure[idx] - pressure(i-1,j,k));
+ if (flags.isFluid(i,j-1,k)) vel[idx].y -= (pressure[idx] - pressure(i,j-1,k));
+ if (flags.is3D() && flags.isFluid(i,j,k-1)) vel[idx].z -= (pressure[idx] - pressure(i,j,k-1));
+
+ if (flags.isEmpty(i-1,j,k)) vel[idx].x -= pressure[idx];
+ if (flags.isEmpty(i,j-1,k)) vel[idx].y -= pressure[idx];
+ if (flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx].z -= pressure[idx];
+ }
+ else if (flags.isEmpty(idx))
+ {
+ if (flags.isFluid(i-1,j,k)) vel[idx].x += pressure(i-1,j,k);
+ else vel[idx].x = 0.f;
+ if (flags.isFluid(i,j-1,k)) vel[idx].y += pressure(i,j-1,k);
+ else vel[idx].y = 0.f;
+ if (flags.is3D() ) {
+ if (flags.isFluid(i,j,k-1)) vel[idx].z += pressure(i,j,k-1);
+ else vel[idx].z = 0.f;
+ }
+ }
+}
+
+//! Kernel: Set matrix stencils and velocities to enable open boundaries
+KERNEL void SetOpenBound(Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, MACGrid& vel,
+ Vector3D<bool> lowerBound, Vector3D<bool> upperBound)
+{
+ // set velocity boundary conditions
+ if (lowerBound.x && i == 0) vel(0,j,k) = vel(1,j,k);
+ if (lowerBound.y && j == 0) vel(i,0,k) = vel(i,1,k);
+ if (upperBound.x && i == maxX-1) vel(maxX-1,j,k) = vel(maxX-2,j,k);
+ if (upperBound.y && j == maxY-1) vel(i,maxY-1,k) = vel(i,maxY-2,k);
+ if(vel.is3D()) {
+ if (lowerBound.z && k == 0) vel(i,j,0) = vel(i,j,1);
+ if (upperBound.z && k == maxZ-1) vel(i,j,maxZ-1) = vel(i,j,maxZ-2);
+ }
+
+ // set matrix stencils at boundary
+ if ((lowerBound.x && i<=1) || (upperBound.x && i>=maxX-2) ||
+ (lowerBound.y && j<=1) || (upperBound.y && j>=maxY-2) ||
+ (lowerBound.z && k<=1) || (upperBound.z && k>=maxZ-2)) {
+ A0(i,j,k) = vel.is3D() ? 6.0 : 4.0;
+ Ai(i,j,k) = -1.0;
+ Aj(i,j,k) = -1.0;
+ if (vel.is3D()) Ak(i,j,k) = -1.0;
+ }
+}
+
+//! Kernel: Set matrix rhs for outflow
+KERNEL void SetOutflow (Grid<Real>& rhs, Vector3D<bool> lowerBound, Vector3D<bool> upperBound, int height)
+{
+ if ((lowerBound.x && i < height) || (upperBound.x && i >= maxX-1-height) ||
+ (lowerBound.y && j < height) || (upperBound.y && j >= maxY-1-height) ||
+ (lowerBound.z && k < height) || (upperBound.z && k >= maxZ-1-height))
+ rhs(i,j,k) = 0;
+}
+
+
+// *****************************************************************************
+// Ghost fluid helpers
+
+// calculate fraction filled with liquid (note, assumes inside value is < outside!)
+inline static Real thetaHelper(Real inside, Real outside)
+{
+ Real denom = inside-outside;
+ if (denom > -1e-04) return 0.5; // should always be neg, and large enough...
+ return std::max(Real(0), std::min(Real(1), inside/denom));
+}
+
+// calculate ghost fluid factor, cell at idx should be a fluid cell
+inline static Real ghostFluidHelper(int idx, int offset, const Grid<Real> &phi, Real gfClamp)
+{
+ Real alpha = thetaHelper(phi[idx], phi[idx+offset]);
+ if (alpha < gfClamp) return alpha = gfClamp;
+ return (1-(1/alpha));
+}
+
+//! Kernel: Adapt A0 for ghost fluid
+KERNEL(bnd=1)
+void ApplyGhostFluidDiagonal(Grid<Real> &A0, const FlagGrid &flags, const Grid<Real> &phi, Real gfClamp)
+{
+ const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ();
+ int idx = flags.index(i,j,k);
+ if (!flags.isFluid(idx)) return;
+
+ if (flags.isEmpty(i-1,j,k)) A0[idx] -= ghostFluidHelper(idx, -X, phi, gfClamp);
+ if (flags.isEmpty(i+1,j,k)) A0[idx] -= ghostFluidHelper(idx, +X, phi, gfClamp);
+ if (flags.isEmpty(i,j-1,k)) A0[idx] -= ghostFluidHelper(idx, -Y, phi, gfClamp);
+ if (flags.isEmpty(i,j+1,k)) A0[idx] -= ghostFluidHelper(idx, +Y, phi, gfClamp);
+ if (flags.is3D()) {
+ if (flags.isEmpty(i,j,k-1)) A0[idx] -= ghostFluidHelper(idx, -Z, phi, gfClamp);
+ if (flags.isEmpty(i,j,k+1)) A0[idx] -= ghostFluidHelper(idx, +Z, phi, gfClamp);
+ }
+}
+
+//! Kernel: Apply velocity update: ghost fluid contribution
+KERNEL(bnd=1)
+void CorrectVelocityGhostFluid(MACGrid &vel, const FlagGrid &flags, const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp)
+{
+ const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ();
+ const int idx = flags.index(i,j,k);
+ if (flags.isFluid(idx))
+ {
+ if (flags.isEmpty(i-1,j,k)) vel[idx][0] += pressure[idx] * ghostFluidHelper(idx, -X, phi, gfClamp);
+ if (flags.isEmpty(i,j-1,k)) vel[idx][1] += pressure[idx] * ghostFluidHelper(idx, -Y, phi, gfClamp);
+ if (flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx][2] += pressure[idx] * ghostFluidHelper(idx, -Z, phi, gfClamp);
+ }
+ else if (flags.isEmpty(idx))
+ {
+ if (flags.isFluid(i-1,j,k)) vel[idx][0] -= pressure(i-1,j,k) * ghostFluidHelper(idx-X, +X, phi, gfClamp);
+ else vel[idx].x = 0.f;
+ if (flags.isFluid(i,j-1,k)) vel[idx][1] -= pressure(i,j-1,k) * ghostFluidHelper(idx-Y, +Y, phi, gfClamp);
+ else vel[idx].y = 0.f;
+ if (flags.is3D() ) {
+ if (flags.isFluid(i,j,k-1)) vel[idx][2] -= pressure(i,j,k-1) * ghostFluidHelper(idx-Z, +Z, phi, gfClamp);
+ else vel[idx].z = 0.f;
+ }
+ }
+}
+
+
+// improve behavior of clamping for large time steps:
+
+inline static Real ghostFluidWasClamped(int idx, int offset, const Grid<Real> &phi, Real gfClamp)
+{
+ Real alpha = thetaHelper(phi[idx], phi[idx+offset]);
+ if (alpha < gfClamp) return true;
+ return false;
+}
+
+KERNEL(bnd=1)
+void ReplaceClampedGhostFluidVels(MACGrid &vel, FlagGrid &flags,
+ const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp )
+{
+ const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ();
+ const int idx = flags.index(i,j,k);
+ if (flags.isFluid(idx))
+ {
+ if( (flags.isEmpty(i-1,j,k)) && (ghostFluidWasClamped(idx, -X, phi, gfClamp)) )
+ vel[idx-X][0] = vel[idx][0];
+ if( (flags.isEmpty(i,j-1,k)) && (ghostFluidWasClamped(idx, -Y, phi, gfClamp)) )
+ vel[idx-Y][1] = vel[idx][1];
+ if( flags.is3D() &&
+ (flags.isEmpty(i,j,k-1)) && (ghostFluidWasClamped(idx, -Z, phi, gfClamp)) )
+ vel[idx-Z][2] = vel[idx][2];
+ }
+ else if (flags.isEmpty(idx))
+ {
+ if( (i>-1) && (flags.isFluid(i-1,j,k)) && ( ghostFluidWasClamped(idx-X, +X, phi, gfClamp) ) )
+ vel[idx][0] = vel[idx-X][0];
+ if( (j>-1) && (flags.isFluid(i,j-1,k)) && ( ghostFluidWasClamped(idx-Y, +Y, phi, gfClamp) ) )
+ vel[idx][1] = vel[idx-Y][1];
+ if( flags.is3D() &&
+ ( (k>-1) && (flags.isFluid(i,j,k-1)) && ( ghostFluidWasClamped(idx-Z, +Z, phi, gfClamp) ) ))
+ vel[idx][2] = vel[idx-Z][2];
+ }
+}
+
+
+// *****************************************************************************
+// Main pressure solve
+
+inline void convertDescToVec(const string& desc, Vector3D<bool>& lo, Vector3D<bool>& up) {
+ for(size_t i=0; i<desc.size(); i++) {
+ if (desc[i] == 'x') lo.x = true;
+ else if (desc[i] == 'y') lo.y = true;
+ else if (desc[i] == 'z') lo.z = true;
+ else if (desc[i] == 'X') up.x = true;
+ else if (desc[i] == 'Y') up.y = true;
+ else if (desc[i] == 'Z') up.z = true;
+ else errMsg("invalid character in boundary description string. Only [xyzXYZ] allowed.");
+ }
+}
+
+//! Perform pressure projection of the velocity grid
+PYTHON void solvePressure(MACGrid& vel, Grid<Real>& pressure, FlagGrid& flags,
+ Grid<Real>* phi = 0,
+ Grid<Real>* perCellCorr = 0,
+ Real gfClamp = 1e-04,
+ Real cgMaxIterFac = 1.5,
+ Real cgAccuracy = 1e-3,
+ string openBound = "",
+ string outflow = "",
+ int outflowHeight = 1,
+ bool precondition = true,
+ bool enforceCompatibility = false,
+ bool useResNorm = true )
+{
+ // parse strings
+ Vector3D<bool> loOpenBound, upOpenBound, loOutflow, upOutflow;
+ convertDescToVec(openBound, loOpenBound, upOpenBound);
+ convertDescToVec(outflow, loOutflow, upOutflow);
+ if (vel.is2D() && (loOpenBound.z || upOpenBound.z))
+ errMsg("open boundaries for z specified for 2D grid");
+
+ // reserve temp grids
+ FluidSolver* parent = flags.getParent();
+ Grid<Real> rhs(parent);
+ Grid<Real> residual(parent);
+ Grid<Real> search(parent);
+ Grid<Real> A0(parent);
+ Grid<Real> Ai(parent);
+ Grid<Real> Aj(parent);
+ Grid<Real> Ak(parent);
+ Grid<Real> tmp(parent);
+ Grid<Real> pca0(parent);
+ Grid<Real> pca1(parent);
+ Grid<Real> pca2(parent);
+ Grid<Real> pca3(parent);
+
+ // setup matrix and boundaries
+ MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak);
+ SetOpenBound (A0, Ai, Aj, Ak, vel, loOpenBound, upOpenBound);
+
+ if (phi) {
+ ApplyGhostFluidDiagonal(A0, flags, *phi, gfClamp);
+ }
+
+ // compute divergence and init right hand side
+ MakeRhs kernMakeRhs (flags, rhs, vel, perCellCorr);
+
+ if (!outflow.empty())
+ SetOutflow (rhs, loOutflow, upOutflow, outflowHeight);
+
+ if (enforceCompatibility)
+ rhs += (Real)(-kernMakeRhs.sum / (Real)kernMakeRhs.cnt);
+
+ // CG setup
+ // note: the last factor increases the max iterations for 2d, which right now can't use a preconditioner
+ const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4);
+ GridCgInterface *gcg;
+ if (vel.is3D())
+ gcg = new GridCg<ApplyMatrix>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak );
+ else
+ gcg = new GridCg<ApplyMatrix2D>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak );
+
+ gcg->setAccuracy( cgAccuracy );
+ gcg->setUseResNorm( useResNorm );
+
+ // optional preconditioning
+ gcg->setPreconditioner( precondition ? GridCgInterface::PC_mICP : GridCgInterface::PC_None, &pca0, &pca1, &pca2, &pca3);
+
+ for (int iter=0; iter<maxIter; iter++) {
+ if (!gcg->iterate()) iter=maxIter;
+ }
+ debMsg("FluidSolver::solvePressure iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1);
+ delete gcg;
+
+ CorrectVelocity(flags, vel, pressure);
+ if (phi) {
+ CorrectVelocityGhostFluid (vel, flags, pressure, *phi, gfClamp);
+ // improve behavior of clamping for large time steps:
+ ReplaceClampedGhostFluidVels (vel, flags, pressure, *phi, gfClamp);
+ }
+}
+
+} // end namespace
+
diff --git a/source/blender/python/manta_full/source/plugin/vortexplugins.cpp b/source/blender/python/manta_full/source/plugin/vortexplugins.cpp new file mode 100644 index 00000000000..fed847ca2bd --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/vortexplugins.cpp @@ -0,0 +1,312 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugins for using vortex sheet meshes + * + ******************************************************************************/ + +#include <iostream> +#include "vortexsheet.h" +#include "vortexpart.h" +#include "shapes.h" +#include "commonkernels.h" +#include "conjugategrad.h" +#include "randomstream.h" +#include "levelset.h" + +using namespace std; + +namespace Manta { + +//! Mark area of mesh inside shape as fixed nodes. +//! Remove all other fixed nodes if 'exclusive' is set +PYTHON void markAsFixed(Mesh& mesh, Shape* shape, bool exclusive=true) +{ + for (int i=0; i<mesh.numNodes(); i++) { + if (shape->isInside(mesh.nodes(i).pos)) + mesh.nodes(i).flags |= Mesh::NfFixed; + else if (exclusive) + mesh.nodes(i).flags &= ~Mesh::NfFixed; + } +} + +//! Adapt texture coordinates of mesh inside shape +//! to obtain an effective inflow effect +PYTHON void texcoordInflow(VortexSheetMesh& mesh, Shape* shape, MACGrid& vel) +{ + static Vec3 t0 = Vec3::Zero; + + // get mean velocity + int cnt=0; + Vec3 meanV(_0); + FOR_IJK(vel) { + if (shape->isInsideGrid(i,j,k)) { + cnt++; + meanV += vel.getCentered(i,j,k); + } + } + meanV /= (Real) cnt; + t0 -= mesh.getParent()->getDt() * meanV; + mesh.setReferenceTexOffset(t0); + + // apply mean velocity + for (int i=0; i<mesh.numNodes(); i++) { + if (shape->isInside(mesh.nodes(i).pos)) { + Vec3 tc = mesh.nodes(i).pos + t0; + mesh.tex1(i) = tc; + mesh.tex2(i) = tc; + } + } +}; + +//! Init smoke density values of the mesh surface inside source shape +PYTHON void meshSmokeInflow(VortexSheetMesh& mesh, Shape* shape, Real amount) +{ + for (int t=0; t<mesh.numTris(); t++) { + if (shape->isInside(mesh.getFaceCenter(t))) + mesh.sheet(t).smokeAmount = amount; + } +} + +KERNEL(idx) +void KnAcceleration(MACGrid& a, const MACGrid& v1, const MACGrid& v0, const Real idt) { + a[idx] = (v1[idx]-v0[idx])*idt; +} + +//! Add vorticity to vortex sheets based on buoyancy +PYTHON void vorticitySource(VortexSheetMesh& mesh, Vec3 gravity, + MACGrid* vel=NULL, MACGrid* velOld=NULL, + Real scale = 0.1, Real maxAmount = 0, Real mult = 1.0) +{ + Real dt = mesh.getParent()->getDt(); + Real dx = mesh.getParent()->getDx(); + MACGrid acceleration(mesh.getParent()); + if (vel) + KnAcceleration(acceleration, *vel, *velOld, 1.0/dt); + const Real A= -1.0; + Real maxV = 0, meanV = 0; + + for (int t=0; t<mesh.numTris(); t++) { + Vec3 fn = mesh.getFaceNormal(t); + Vec3 source; + if (vel) { + Vec3 a = acceleration.getInterpolated(mesh.getFaceCenter(t)); + source = A*cross(fn, a-gravity) * scale; + } else { + source = A*cross(fn, -gravity) * scale; + } + + if (mesh.isTriangleFixed(t)) source = 0; + + mesh.sheet(t).vorticity *= mult; + mesh.sheet(t).vorticity += dt * source / dx; + // upper limit + Real v = norm(mesh.sheet(t).vorticity); + if (maxAmount>0 && v > maxAmount) + mesh.sheet(t).vorticity *= maxAmount/v; + + //stats + if (v > maxV) maxV = v; + meanV += v; + } + + cout << "vorticity: max " << maxV << " / mean " << meanV/mesh.numTris() << endl; +} + +PYTHON void smoothVorticity(VortexSheetMesh& mesh, int iter=1, Real sigma=0.2, Real alpha=0.8) +{ + const Real mult = -0.5 / sigma / sigma; + + // pre-calculate positions and weights + vector<Vec3> vort(mesh.numTris()), pos(mesh.numTris()); + vector<Real> weights(3*mesh.numTris()); + vector<int> index(3*mesh.numTris()); + for(int i=0; i<mesh.numTris(); i++) { + pos[i] = mesh.getFaceCenter(i); + mesh.sheet(i).vorticitySmoothed = mesh.sheet(i).vorticity; + } + for(int i=0; i<mesh.numTris(); i++) { + for (int c=0; c<3; c++) { + int oc = mesh.corners(i,c).opposite; + if (oc>=0) { + int t = mesh.corners(oc).tri; + weights[3*i+c] = exp(normSquare(pos[t]-pos[i])*mult); + index[3*i+c] = t; + } + else { + weights[3*i+c] = 0; + index[3*i+c] = 0; + } + } + } + + for (int it=0; it<iter; ++it) { + // first, preload + for(int i=0; i<mesh.numTris(); i++) vort[i] = mesh.sheet(i).vorticitySmoothed; + + for(int i=0,idx=0; i<mesh.numTris(); i++) { + // loop over adjacent tris + Real sum=1.0f; + Vec3 v=vort[i]; + for (int c=0;c<3;c++,idx++) { + Real w = weights[index[idx]]; + v += w*vort[index[idx]]; + sum += w; + } + mesh.sheet(i).vorticitySmoothed = v/sum; + } + } + for(int i=0; i<mesh.numTris(); i++) mesh.sheet(i).vorticitySmoothed *= alpha; +} + +//! Seed Vortex Particles inside shape with K41 characteristics +PYTHON void VPseedK41(VortexParticleSystem& system, Shape* shape, Real strength=0, Real sigma0=0.2, Real sigma1=1.0, Real probability=1.0, Real N=3.0) { + Grid<Real> temp(system.getParent()); + const Real dt = system.getParent()->getDt(); + static RandomStream rand(3489572); + Real s0 = pow( (Real)sigma0, (Real)(-N+1.0) ); + Real s1 = pow( (Real)sigma1, (Real)(-N+1.0) ); + + FOR_IJK(temp) { + if (shape->isInsideGrid(i,j,k)) { + if (rand.getReal() < probability*dt) { + Real p = rand.getReal(); + Real sigma = pow( (1.0-p)*s0 + p*s1, 1./(-N+1.0) ); + Vec3 randDir (rand.getReal(), rand.getReal(), rand.getReal()); + Vec3 posUpd (i+rand.getReal(), j+rand.getReal(), k+rand.getReal()); + normalize(randDir); + Vec3 vorticity = randDir * strength * pow( (Real)sigma, (Real)(-10./6.+N/2.0) ); + system.add(VortexParticleData(posUpd, vorticity, sigma)); + } + } + } +} + +//! Vortex-in-cell integration +PYTHON void VICintegration(VortexSheetMesh& mesh, Real sigma, Grid<Vec3>& vel, FlagGrid& flags, + Grid<Vec3>* vorticity=NULL, Real cgMaxIterFac=1.5, Real cgAccuracy=1e-3, Real scale = 0.01, int precondition=0) { + + MuTime t0; + const Real fac = 16.0; // experimental factor to balance out regularization + + // if no vort grid is given, use a temporary one + Grid<Vec3> vortTemp(mesh.getParent()); + Grid<Vec3>& vort = (vorticity) ? (*vorticity) : (vortTemp); + vort.clear(); + + // map vorticity to grid using Peskin kernel + int sgi = ceil(sigma); + Real pkfac=M_PI/sigma; + const int numTris = mesh.numTris(); + for (int t=0; t<numTris; t++) { + Vec3 pos = mesh.getFaceCenter(t); + Vec3 v = mesh.sheet(t).vorticity * mesh.getFaceArea(t) * fac; + + // inner kernel + // first, summate + Real sum=0; + for (int i=-sgi; i<sgi; i++) { + if (pos.x+i < 0 || (int)pos.x+i >= vort.getSizeX()) continue; + for (int j=-sgi; j<sgi; j++) { + if (pos.y+j < 0 || (int)pos.y+j >= vort.getSizeY()) continue; + for (int k=-sgi; k<sgi; k++) { + if (pos.z+k < 0 || (int)pos.z+k >= vort.getSizeZ()) continue; + Vec3i cell(pos.x+i, pos.y+j, pos.z+k); + if (!flags.isFluid(cell)) continue; + Vec3 d = pos - Vec3(i+0.5+floor(pos.x), j+0.5+floor(pos.y), k+0.5+floor(pos.z)); + Real dl = norm(d); + if (dl > sigma) continue; + // precalc Peskin kernel + sum += 1.0 + cos(dl * pkfac); + } + } + } + // then, apply normalized kernel + Real wnorm = 1.0/sum; + for (int i=-sgi; i<sgi; i++) { + if (pos.x+i < 0 || (int)pos.x+i >= vort.getSizeX()) continue; + for (int j=-sgi; j<sgi; j++) { + if (pos.y+j < 0 || (int)pos.y+j >= vort.getSizeY()) continue; + for (int k=-sgi; k<sgi; k++) { + if (pos.z+k < 0 || (int)pos.z+k >= vort.getSizeZ()) continue; + Vec3i cell(pos.x+i, pos.y+j, pos.z+k); + if (!flags.isFluid(cell)) continue; + Vec3 d = pos - Vec3(i+0.5+floor(pos.x), j+0.5+floor(pos.y), k+0.5+floor(pos.z)); + Real dl = norm(d); + if (dl > sigma) continue; + Real w = (1.0 + cos(dl * pkfac))*wnorm; + vort(cell) += v * w; + } + } + } + } + + // Prepare grids for poisson solve + Grid<Vec3> vortexCurl(mesh.getParent()); + Grid<Real> rhs(mesh.getParent()); + Grid<Real> solution(mesh.getParent()); + Grid<Real> residual(mesh.getParent()); + Grid<Real> search(mesh.getParent()); + Grid<Real> temp1(mesh.getParent()); + Grid<Real> A0(mesh.getParent()); + Grid<Real> Ai(mesh.getParent()); + Grid<Real> Aj(mesh.getParent()); + Grid<Real> Ak(mesh.getParent()); + Grid<Real> pca0(mesh.getParent()); + Grid<Real> pca1(mesh.getParent()); + Grid<Real> pca2(mesh.getParent()); + Grid<Real> pca3(mesh.getParent()); + + MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak); + CurlOp(vort, vortexCurl); + + // Solve vector poisson equation + for (int c=0; c<3; c++) { + // construct rhs + if (vel.getType() & GridBase::TypeMAC) + GetShiftedComponent(vortexCurl, rhs, c); + else + GetComponent(vortexCurl, rhs, c); + + // prepare CG solver + const int maxIter = (int)(cgMaxIterFac * vel.getSize().max()); + GridCgInterface *gcg = new GridCg<ApplyMatrix>(solution, rhs, residual, search, flags, temp1, &A0, &Ai, &Aj, &Ak ); + gcg->setAccuracy(cgAccuracy); + gcg->setUseResNorm(true); + gcg->setPreconditioner( (GridCgInterface::PreconditionType)precondition, &pca0, &pca1, &pca2, &pca3); + + // iterations + for (int iter=0; iter<maxIter; iter++) { + if (!gcg->iterate()) iter=maxIter; + } + debMsg("VICintegration CG iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1); + delete gcg; + + // copy back + solution *= scale; + SetComponent(vel, solution, c); + } +} + +//! Obtain density field from levelset with linear gradient of size sigma over the interface +PYTHON void densityFromLevelset(LevelsetGrid& phi, Grid<Real>& density, Real value=1.0, Real sigma=1.0) { + FOR_IJK(phi) { + // remove boundary + if (i<2 || j<2 || k<2 || i>=phi.getSizeX()-2 || j>=phi.getSizeY()-2 || k>=phi.getSizeZ()-2) + density(i,j,k) = 0; + else if (phi(i,j,k) < -sigma) + density(i,j,k) = value; + else if (phi(i,j,k) > sigma) + density(i,j,k) = 0; + else + density(i,j,k) = clamp((Real)(0.5*value/sigma*(1.0-phi(i,j,k))), _0, value); + } +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/plugin/waveletturbulence.cpp b/source/blender/python/manta_full/source/plugin/waveletturbulence.cpp new file mode 100644 index 00000000000..2b496da717c --- /dev/null +++ b/source/blender/python/manta_full/source/plugin/waveletturbulence.cpp @@ -0,0 +1,296 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Functions for calculating wavelet turbulence, + * plus helpers to compute vorticity, and strain rate magnitude + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "noisefield.h" + +using namespace std; + +namespace Manta { + + +//! Apply vector noise to grid, this is a simplified version - no position scaling or UVs +KERNEL +void knApplySimpleNoiseVec(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, + Real scale, Grid<Real>* weight ) +{ + if ( !flags.isFluid(i,j,k) ) return; + Real factor = 1; + if(weight) factor = (*weight)(i,j,k); + target(i,j,k) += noise.evaluateCurl( Vec3(i,j,k) ) * scale * factor; +} +PYTHON void applySimpleNoiseVec3(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, + Real scale=1.0 , Grid<Real>* weight=NULL ) +{ + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component separately + knApplySimpleNoiseVec(flags, target, noise, scale , weight ); +} + + +//! Simple noise for a real grid , follows applySimpleNoiseVec3 +KERNEL +void knApplySimpleNoiseReal(FlagGrid& flags, Grid<Real>& target, WaveletNoiseField& noise, + Real scale, Grid<Real>* weight ) +{ + if ( !flags.isFluid(i,j,k) ) return; + Real factor = 1; + if(weight) factor = (*weight)(i,j,k); + target(i,j,k) += noise.evaluate( Vec3(i,j,k) ) * scale * factor; +} +PYTHON void applySimpleNoiseReal(FlagGrid& flags, Grid<Real>& target, WaveletNoiseField& noise, + Real scale=1.0 , Grid<Real>* weight=NULL ) +{ + knApplySimpleNoiseReal(flags, target, noise, scale , weight ); +} + + + +//! Apply vector-based wavelet noise to target grid +//! This is the version with more functionality - supports uv grids, and on-the-fly interpolation +//! of input grids. +KERNEL +void knApplyNoiseVec(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, + Real scale, Real scaleSpatial, Grid<Real>* weight, Grid<Vec3>* uv, bool uvInterpol, const Vec3& sourceFactor ) +{ + if ( !flags.isFluid(i,j,k) ) return; + + // get weighting, interpolate if necessary + Real w = 1; + if(weight) { + if(!uvInterpol) { + w = (*weight)(i,j,k); + } else { + w = weight->getInterpolated( Vec3(i,j,k) * sourceFactor ); + } + } + + // compute position where to evaluate the noise + Vec3 pos = Vec3(i,j,k); + if(uv) { + if(!uvInterpol) { + pos = (*uv)(i,j,k); + } else { + pos = uv->getInterpolated( Vec3(i,j,k) * sourceFactor ); + // uv coordinates are in local space - so we need to adjust the values of the positions + pos /= sourceFactor; + } + } + pos *= scaleSpatial; + + Vec3 noiseVec3 = noise.evaluateCurl( pos ) * scale * w; + //noiseVec3=pos; // debug , show interpolated positions + target(i,j,k) += noiseVec3; +} +PYTHON void applyNoiseVec3(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, + Real scale=1.0 , Real scaleSpatial=1.0 , Grid<Real>* weight=NULL , Grid<Vec3>* uv=NULL ) +{ + // check whether the uv grid has a different resolution + bool uvInterpol = false; + // and pre-compute conversion (only used if uvInterpol==true) + // used for both uv and weight grid... + Vec3 sourceFactor = Vec3(1.); + if(uv) { + uvInterpol = (target.getSize() != uv->getSize()); + sourceFactor = calcGridSizeFactor( uv->getSize(), target.getSize() ); + } else if(weight) { + uvInterpol = (target.getSize() != weight->getSize()); + sourceFactor = calcGridSizeFactor( weight->getSize(), target.getSize() ); + } + if(uv && weight) assertMsg( uv->getSize() == weight->getSize(), "UV and weight grid have to match!"); + + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component separately + knApplyNoiseVec(flags, target, noise, scale, scaleSpatial, weight , uv,uvInterpol,sourceFactor ); +} + + + +//! Compute energy of a staggered velocity field (at cell center) +KERNEL +void KnApplyComputeEnergy( FlagGrid& flags, MACGrid& vel, Grid<Real>& energy ) +{ + Real e = 0.f; + if ( flags.isFluid(i,j,k) ) { + Vec3 v = vel.getCentered(i,j,k); + e = 0.5 * v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + } + energy(i,j,k) = e; +} + +PYTHON void computeEnergy( FlagGrid& flags, MACGrid& vel, Grid<Real>& energy ) +{ + KnApplyComputeEnergy( flags, vel, energy ); +} + + + +//!interpolate grid from one size to another size +KERNEL +void KnInterpolateGrid(Grid<Real>& target, Grid<Real>& source, const Vec3& sourceFactor) +{ + Vec3 pos = Vec3(i,j,k) * sourceFactor; + if(!source.is3D()) pos[2] = 0; // allow 2d -> 3d + target(i,j,k) = source.getInterpolated(pos); +} + +PYTHON void interpolateGrid( Grid<Real>& target, Grid<Real>& source ) +{ + Vec3 sourceFactor = calcGridSizeFactor( source.getSize(), target.getSize() ); + + // a brief note on a mantaflow specialty: the target grid has to be the first argument here! + // the parent fluidsolver object is taken from the first grid, and it determines the size of the + // loop for the kernel call. as we're writing into target, it's important to loop exactly over + // all cells of the target grid... (note, when calling the plugin in python, it doesnt matter anymore). + + KnInterpolateGrid(target, source, sourceFactor); +} + + +//!interpolate a mac velocity grid from one size to another size +KERNEL +void KnInterpolateMACGrid(MACGrid& target, MACGrid& source, const Vec3& sourceFactor) +{ + Vec3 pos = Vec3(i,j,k) * sourceFactor; + + Real vx = source.getInterpolated(pos - Vec3(0.5,0,0))[0]; + Real vy = source.getInterpolated(pos - Vec3(0,0.5,0))[1]; + Real vz = 0.f; + if(source.is3D()) vz = source.getInterpolated(pos - Vec3(0,0,0.5))[2]; + + target(i,j,k) = Vec3(vx,vy,vz); +} + +PYTHON void interpolateMACGrid(MACGrid& target, MACGrid& source) +{ + Vec3 sourceFactor = calcGridSizeFactor( source.getSize(), target.getSize() ); + + // see interpolateGrid for why the target grid needs to come first in the parameters! + + KnInterpolateMACGrid(target, source, sourceFactor); +} + +PYTHON void computeWaveletCoeffs(Grid<Real>& input) +{ + Grid<Real> temp1(input.getParent()), temp2(input.getParent()); + WaveletNoiseField::computeCoefficients(input, temp1, temp2); +} + +// note - alomst the same as for vorticity confinement +PYTHON void computeVorticity(MACGrid& vel, Grid<Vec3>& vorticity, Grid<Real>* norm) { + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + CurlOp(velCenter, vorticity); + if(norm) GridNorm( *norm, vorticity); +} + +// note - very similar to KnComputeProductionStrain, but for use as wavelet turb weighting +KERNEL(bnd=1) +void KnComputeStrainRateMag(const MACGrid& vel, const Grid<Vec3>& velCenter, Grid<Real>& prod ) +{ + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, 0. ) - vel(i,j,k); + if(vel.is3D()) diag[2] += vel(i,j,k+1).z; + else diag[2] = 0.; + + Vec3 ux = 0.5*(velCenter(i+1,j,k)-velCenter(i-1,j,k)); + Vec3 uy = 0.5*(velCenter(i,j+1,k)-velCenter(i,j-1,k)); + Vec3 uz; + if(vel.is3D()) uz=0.5*(velCenter(i,j,k+1)-velCenter(i,j,k-1)); + + Real S12 = 0.5*(ux.y+uy.x); + Real S13 = 0.5*(ux.z+uz.x); + Real S23 = 0.5*(uy.z+uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + + 2.0*square(S12) + 2.0*square(S13) + 2.0*square(S23); + prod(i,j,k) = S2; +} +PYTHON void computeStrainRateMag(MACGrid& vel, Grid<Real>& mag) { + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + KnComputeStrainRateMag(vel, velCenter, mag); +} + + +// extrapolate a real grid into a flagged region (based on initial flags) +// by default extrapolates from fluid to obstacle cells +template<class T> +void extrapolSimpleFlagsHelper (FlagGrid& flags, Grid<T>& val, int distance = 4, + int flagFrom=FlagGrid::TypeFluid, int flagTo=FlagGrid::TypeObstacle ) +{ + Grid<int> tmp( flags.getParent() ); + int dim = (flags.is3D() ? 3:2); + const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + + // remove all fluid cells (set to 1) + tmp.clear(); + bool foundTarget = false; + FOR_IJK_BND(flags,0) { + if (flags(i,j,k) & flagFrom) + tmp( Vec3i(i,j,k) ) = 1; + if (!foundTarget && (flags(i,j,k) & flagTo)) foundTarget=true; + } + // optimization, skip extrapolation if we dont have any cells to extrapolate to + if(!foundTarget) { + debMsg("No target cells found, skipping extrapolation", 1); + return; + } + + // extrapolate for given distance + for(int d=1; d<1+distance; ++d) { + + // TODO, parallelize + FOR_IJK_BND(flags,1) { + if (tmp(i,j,k) != 0) continue; + if (!(flags(i,j,k) & flagTo)) continue; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + T avgVal = 0.; + for (int n=0; n<2*dim; ++n) { + if (tmp(p+nb[n]) == d) { + avgVal += val(p+nb[n]); + nbs++; + } + } + + if(nbs>0) { + tmp(p) = d+1; + val(p) = avgVal / nbs; + } + } + + } // distance +} +PYTHON void extrapolateSimpleFlags (FlagGrid& flags, GridBase* val, int distance = 4, + int flagFrom=FlagGrid::TypeFluid, int flagTo=FlagGrid::TypeObstacle ) +{ + if (val->getType() & GridBase::TypeReal) { + extrapolSimpleFlagsHelper<Real>(flags,*((Grid<Real>*) val),distance,flagFrom,flagTo); + } + else if (val->getType() & GridBase::TypeInt) { + extrapolSimpleFlagsHelper<int >(flags,*((Grid<int >*) val),distance,flagFrom,flagTo); + } + else if (val->getType() & GridBase::TypeVec3) { + extrapolSimpleFlagsHelper<Vec3>(flags,*((Grid<Vec3>*) val),distance,flagFrom,flagTo); + } + else + errMsg("extrapolateSimpleFlags: Grid Type is not supported (only int, Real, Vec3)"); +} + +} // namespace diff --git a/source/blender/python/manta_full/source/preprocessor/code.cpp b/source/blender/python/manta_full/source/preprocessor/code.cpp new file mode 100644 index 00000000000..9937b37a772 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/code.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Code Structs + * + ******************************************************************************/ + +#include <iostream> +#include <cstdlib> +#include <algorithm> +#include "prep.h" + +using namespace std; + +//************************************************************* +// Helpers + +bool Type::operator==(const Type& a) const { + if (a.templateTypes.size() != templateTypes.size()) return false; + for (size_t i=0; i<templateTypes.size(); i++) + if (templateTypes[i].name != a.templateTypes[i].name) return false; + return a.name==name && a.isConst==isConst && a.isRef==isRef && a.isPointer==isPointer; +} + +string Type::build(bool refify) const { + string s=""; + if (isConst) s+= "const "; + s += name; + if (!templateTypes.empty()) + s+= templateTypes.minimal; + if (isRef || (refify && !isPointer)) s+= "&"; + if (isPointer) s+= "*"; + return s; +} + +string Text::linebreaks() const { + int num = count(original.begin(), original.end(), '\n') - + count(minimal.begin(),minimal.end(), '\n'); + string s=""; + for (int i=0; i< num; i++) + s += '\n'; + return s; +} + +string Function::signature() const { + string s; + if (isTemplated()) + s = "template " + templateTypes.minimal; + if (isVirtual) + s += "virtual "; + if (isInline) + s += "inline "; + s+= returnType.minimal + name + arguments.minimal; + if (isConst) + s += "const "; + return s; +} + +template<> std::string List<Argument>::full(bool refify) const { + std::stringstream s; + for (int i=0; i<(int)_data.size(); i++) { + s << "," << _data[i].type.build(refify) << " " << _data[i].name; + } + return s.str(); +} + +template<> std::string List<Argument>::createMembers(bool refify) const { + std::stringstream s; + for (int i=0; i<(int)_data.size(); i++) + s << _data[i].type.build(refify) << ' ' << _data[i].name << "; "; + return s.str(); +} + +template<> std::string List<Argument>::copier(const std::string& prefix, bool useVal) const { + std::stringstream s; + for (int i=0; i<(int)_data.size(); i++) { + s << ',' << _data[i].name << '('; + s << (useVal ? _data[i].value : prefix+_data[i].name); + s << ')'; + } + return s.str(); +} + +template struct List<Argument>; +template struct List<Type>; + diff --git a/source/blender/python/manta_full/source/preprocessor/code.h b/source/blender/python/manta_full/source/preprocessor/code.h new file mode 100644 index 00000000000..5dfe76eedfa --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/code.h @@ -0,0 +1,134 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Declarations + * + ******************************************************************************/ + +#ifndef _CODE_H +#define _CODE_H + +#include <string> +#include <vector> + +struct Text { + int line0; + std::string minimal, original; + void reset() { minimal = original = ""; line0=0; } + std::string linebreaks() const; + virtual std::string dynamicClass() { return ""; } + void prequel(const Text* a) { minimal = a->minimal + minimal; original = a->original + original; } + void post(const Text* a) { minimal += a->minimal; original += a->original; } +}; + +template <class T> +struct List : Text { + std::vector<T> _data; + std::string listText; + + // exposed vector members + inline size_t size() const { return _data.size(); } + inline T& operator[](int i) { return _data[i]; } + inline const T& operator[](int i) const { return _data[i]; } + inline bool empty() const { return _data.empty(); } + inline T& back() { return _data.back(); } + inline void push_back(const T& a) { _data.push_back(a); } + + std::string names() const; + std::string full(bool refify=false) const { return ""; }; + std::string copier(const std::string& prefix, bool useValue) const { return ""; }; + std::string createMembers(bool refify = false) const { return ""; }; + virtual std::string dynamicClass() { return "List"; } +}; + +struct Type : Text { + Type() : isConst(false), isRef(false), isPointer(false) {}; + + std::string name; + bool isConst, isRef, isPointer; + List<Type> templateTypes; + + inline bool isTemplated() const { return !templateTypes.empty(); } + bool operator==(const Type& a) const; + std::string build(bool refify = false) const; + virtual std::string dynamicClass() { return "Type"; } +}; + +struct Argument : Text { + Argument() : type(),index(-1) {}; + + Type type; + int index; + std::string name, value; + std::string completeText, minimalText; + virtual std::string dynamicClass() { return "Argument"; } +}; + +struct Function : Text { + Function() : returnType(),isInline(false),isVirtual(false),isConst(false),noParentheses(false),isOperator(false) {} + + std::string name; + Type returnType; + bool isInline, isVirtual, isConst, noParentheses, isOperator; + List<Type> templateTypes; + List<Argument> arguments; + std::string signature() const; + inline std::string callString() const { return arguments.names(); } + inline bool isTemplated() const { return !templateTypes.empty(); } + virtual std::string dynamicClass() { return "Function"; } +}; + +struct Instantiation { + Instantiation(const std::string& c, const std::string& n) : + cls(c), name(n) {} + std::string cls, name; + std::vector<std::string> wrapName; + std::vector<Function> func; + std::vector<std::string> templates; +}; + +struct Class : Text { + Class() {}; + + std::string name; + Type baseClass; + List<Type> templateTypes; + std::string fullName() const { return isTemplated() ? (name+"<"+templateTypes.names()+">") : name; } + inline bool isTemplated() const { return !templateTypes.empty(); } + virtual std::string dynamicClass() { return "Class"; } +}; + +struct Block : Text { + Block() {}; + + int line1; + std::string initList; + Class cls; + const Class *parent; + Function func; + List<Argument> options; + List<Argument> locals; + virtual std::string dynamicClass() { return "Block"; } +}; + +// list functions , notify compiler of specializations in code.cpp + +template<class T> std::string List<T>::names() const { + std::stringstream s; + for (int i=0; i<(int)_data.size(); i++) { + s << _data[i].name; + if (i != (int)_data.size()-1) s << ','; + } + return s.str(); +} +template<> std::string List<Argument>::full(bool refify) const; +template<> std::string List<Argument>::createMembers(bool refify) const; +template<> std::string List<Argument>::copier(const std::string& prefix, bool useVal) const; + +#endif diff --git a/source/blender/python/manta_full/source/preprocessor/codegen_kernel.cpp b/source/blender/python/manta_full/source/preprocessor/codegen_kernel.cpp new file mode 100644 index 00000000000..b7505888ac6 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/codegen_kernel.cpp @@ -0,0 +1,365 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor: Process replacement text of KERNEL keyword + * + ******************************************************************************/ + +#include "prep.h" +#include <cstdlib> +#include <set> +#include <sstream> +#include <iostream> +using namespace std; + +#define STR(x) #x + +//****************************************************** +// Templates for code generation + +// TP: why do we need getArg? just directly access the argument via its name... +const string TmpAccessor = STR( +inline $TYPE$ getArg$IDX$() { return $NAME$; } +typedef $TYPE_NOREF$ type$IDX$; +); + +// Single kernel, default +const string TmpSingleKernel = STR( +$TEMPLATE$ struct $KERNEL$ : public KernelBase { + $KERNEL$($ARGS$) : +@IF(PTS) + KernelBase($BASE$.size()) $INIT$ $LOCALSET$ +@ELSE + KernelBase($BASE$,$BND$) $INIT$ $LOCALSET$ +@END + { + run(); + } +@IF(IJK) + inline void op(int i, int j, int k, $ARGS$ $LOCALARG$) $CONST$ $CODE$ +@ELSE + inline void op(int idx, $ARGS$ $LOCALARG$) $CONST$ $CODE$ +@END + +@IF(RET_NAME) + inline operator $RET_TYPE$() { return $RET_NAME$; } + inline $RET_TYPE$ & getRet() { return $RET_NAME$; } +@END + $ACCESSORS$ + + $RUN$ + $MEMBERS$ + $LOCALS$ +}; +); + +// Necesary for TBB with nontrivial return values +const string TmpDoubleKernel = STR( +// inner kernel +$TEMPLATE$ struct _$KERNEL$ : public KernelBase { + _$KERNEL$(const KernelBase& base, $ARGS$ $LOCALARG$) : + KernelBase(base) $INIT$ $LOCALINIT${} + +@IF(IJK) + inline void op(int i, int j, int k, $ARGS$ $LOCALARG$) $CONST$ $CODE$ +@ELSE + inline void op(int idx, $ARGS$ $LOCALARG$) $CONST$ $CODE$ +@END + $RUN$ + $MEMBERS$ + $LOCALS_REF$ +}; + +// outer kernel with accessors +$TEMPLATE$ struct $KERNEL$ : public KernelBase { + $KERNEL$($ARGS$) : +@IF(PTS) + KernelBase($BASE$.size()) $COMMA$ _inner(KernelBase($BASE$.size()),$CALL$) +@ELSE + KernelBase($BASE$,$BND$) $COMMA$ _inner(KernelBase($BASE$,$BND$),$CALL$) +@END + $INIT$ $LOCALSET$ + { + run(); + } + + void run() { _inner.run(); } + +@IF(RET_NAME) + inline operator $RET_TYPE$() { return $RET_NAME$; } + inline $RET_TYPE$ & getRet() { return $RET_NAME$; } +@END + $ACCESSORS$ + _$KERNEL$$TPL$ _inner; + $MEMBERS$ + $LOCALS$ +}; +); + +const string TmpRunSimple = STR( +void run() { +@IF(IJK) + const int _maxX = maxX; + const int _maxY = maxY; + for (int k=minZ; k< maxZ; k++) + for (int j=$BND$; j< _maxY; j++) + for (int i=$BND$; i< _maxX; i++) + op(i,j,k, $CALL$); +@ELSE + const int _sz = size; + for (int i=0; i < _sz; i++) + op(i, $CALL$); +@END +} +); + +const string TmpRunTBB = STR( +void operator() (const tbb::blocked_range<size_t>& r) $CONST$ { +@IF(IJK) + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ>1) { + for (int k=r.begin(); k!=(int)r.end(); k++) + for (int j=$BND$; j<_maxY; j++) + for (int i=$BND$; i<_maxX; i++) + op(i,j,k,$CALL$); + } else { + const int k=0; + for (int j=r.begin(); j!=(int)r.end(); j++) + for (int i=$BND$; i<_maxX; i++) + op(i,j,k,$CALL$); + } +@ELSE + for (int idx=r.begin(); idx!=(int)r.end(); idx++) + op(idx, $CALL$); +@END +} +void run() { +@IF(IJK) + if (maxZ>1) + tbb::parallel_$METHOD$ (tbb::blocked_range<size_t>(minZ, maxZ), *this); + else + tbb::parallel_$METHOD$ (tbb::blocked_range<size_t>($BND$, maxY), *this); +@ELSE + tbb::parallel_$METHOD$ (tbb::blocked_range<size_t>(0, size), *this); +@END +} +@IF(REDUCE) + $IKERNEL$ ($IKERNEL$& o, tbb::split) : KernelBase(o) $COPY$ $LOCALSET$ {} + + void join(const $IKERNEL$ & o) { + $JOINER$ + } +@END +); + +const string TmpRunOMP = STR( +void run() { +@IF(IJK) + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + $PRAGMA$ omp parallel $NL$ + { + $OMP_DIRECTIVE$ + for (int k=minZ; k < maxZ; k++) + for (int j=$BND$; j < _maxY; j++) + for (int i=$BND$; i < _maxX; i++) + op(i,j,k,$CALL$); + $OMP_POST$ + } + } else { + const int k=0; + $PRAGMA$ omp parallel $NL$ + { + $OMP_DIRECTIVE$ + for (int j=$BND$; j < _maxY; j++) + for (int i=$BND$; i < _maxX; i++) + op(i,j,k,$CALL$); + $OMP_POST$ + } + } +@ELSE + const int _sz = size; + $PRAGMA$ omp parallel $NL$ + { + $OMP_DIRECTIVE$ + for (int i=0; i < _sz; i++) + op(i,$CALL$); + $OMP_POST$ + } +@END +} +); + +const string TmpOMPDirective = STR ( +this->threadId = omp_get_thread_num(); +this->threadNum = omp_get_num_threads(); +@IF(REDUCE) + $OMP_PRE$ + $PRAGMA$ omp for nowait $NL$ +@ELSE + $PRAGMA$ omp for $NL$ +@END +); + + +#define kernelAssert(x,msg) if(!(x)){errMsg(block.line0,string("KERNEL: ") + msg);} + +void processKernel(const Block& block, const string& code, Sink& sink) { + const Function& kernel = block.func; + + if (gDocMode) { + sink.inplace << "//! \\ingroup Kernels\n" << block.func.minimal << "{}\n"; + return; + } + + // process options + bool idxMode = false, reduce = false, pts = false; + bool hasLocals = !block.locals.empty(), hasRet = kernel.returnType.name != "void"; + string bnd = "0", reduceOp=""; + + MType mtType = gMTType; + for (size_t i=0; i<block.options.size(); i++) { + const string& opt = block.options[i].name; + if (opt == "ijk") + idxMode = false; + else if (opt == "index" || opt == "idx") + idxMode = true; + else if (opt == "st" || opt == "single") + mtType = MTNone; + else if (opt == "pts" || opt == "particle" || opt == "points") + pts = true; + else if (opt == "bnd") + bnd = block.options[i].value; + else if (opt == "reduce") { + reduce = true; + reduceOp = block.options[i].value; + if (!(reduceOp == "+" || reduceOp == "-" || reduceOp == "*" || + reduceOp == "/" || reduceOp == "min" || reduceOp == "max")) + errMsg(block.line0, "invalid 'reduce' operator. Expected reduce= +|-|*|/|min|max"); + } else + errMsg(block.line0, "illegal kernel option '"+ opt + + "' Supported options are: 'ijk', 'idx', 'bnd=x', 'reduce=x', 'st', 'pts'"); + } + + // point out illegal paramter combinations + kernelAssert (bnd == "0" || !idxMode, "can't combine index mode with bounds iteration."); + kernelAssert (!pts || (!idxMode && bnd == "0" ), + "KERNEL(opt): Modes 'ijk', 'idx' and 'bnd' can't be applied to particle kernels."); + + // check type consistency of first 'returns' with return type + if (hasRet && kernel.returnType.name != "void") { + kernelAssert(block.locals.size() == 1, "multiple returns statement only work for 'void' kernels"); + const Type& rt = block.locals[0].type; + kernelAssert(rt == kernel.returnType, "return type does not match type in first 'returns' statement"); + } + kernelAssert(kernel.returnType.name == "void" || hasRet, + "return argument specified without matching 'returns' initializer"); + + // figure out basegrid + string baseGrid; + for (int i=0; i<(int)kernel.arguments.size(); i++) { + const string& type = kernel.arguments[i].type.name; + bool isGrid = type.find("Grid") != string::npos; + if (isGrid || pts) { + baseGrid = kernel.arguments[i].name; + if (isGrid && !kernel.arguments[i].type.isPointer) + baseGrid = "&"+baseGrid; + break; + } + } + kernelAssert(!baseGrid.empty(), ": use at least one grid to call the kernel."); + + // build accesors + stringstream accessors; + for (int i=0; i<(int)kernel.arguments.size(); i++) { + stringstream num; num << i; + Type noref = kernel.arguments[i].type; + noref.isPointer = noref.isRef = noref.isConst = false; + const string table[] = { "TYPE", kernel.arguments[i].type.build(true), + "TYPE_NOREF", noref.build(), + "NAME", kernel.arguments[i].name, + "IDX", num.str(), + "" }; + accessors << replaceSet(TmpAccessor, table); + } + + // build locals, and reduce joiners + stringstream joiner, preReduce, postReduce; + for (int i=0; i<(int)block.locals.size(); i++) { + const string& name = block.locals[i].name; + const string type = block.locals[i].type.build(); + const string& value = block.locals[i].value; + + preReduce << type << " " << name << " = " << value << ";"; + if (reduceOp == "min" || reduceOp == "max") { + joiner << name << " = " << reduceOp << "(" << name << ",o." << name << "); "; + postReduce << "this->" << name << " = " << reduceOp << "(" << name << ", this->" << name << "); "; + } else { + joiner << name << " " << reduceOp << "= o." << name << "; "; + postReduce << "this->" << name << " " << reduceOp << "= " << name << "; "; + } + } + const string ompPost = reduce ? "\n#pragma omp critical\n{"+postReduce.str()+"}":""; + bool doubleKernel = mtType == MTTBB && hasRet && !reduce; + + const string table[] = { "IDX", idxMode ? "Y":"", + "PTS", pts ? "Y":"", + "IJK", (!pts && !idxMode) ? "Y":"", + "REDUCE", reduce ? "Y":"", + "TEMPLATE", kernel.isTemplated() ? "template "+kernel.templateTypes.minimal : "", + "TPL", kernel.isTemplated() ? "<"+kernel.templateTypes.names()+">" : "", + "KERNEL", kernel.name, + "IKERNEL", (doubleKernel ? "_":"") + kernel.name, + "ARGS", kernel.arguments.listText, + "LOCALARG", block.locals.full(true), + "BASE", baseGrid, + "INIT", kernel.arguments.copier("", false), + "LOCALINIT", block.locals.copier("", false), + "LOCALSET", block.locals.copier("", true), + "COPY", kernel.arguments.copier("o.", false), + "MEMBERS", kernel.arguments.createMembers(), + "LOCALS", block.locals.createMembers(false), + "LOCALS_REF", block.locals.createMembers(true), + "ACCESSORS", accessors.str(), + "CONST", (!reduce && mtType==MTTBB) ? "const" : "", + "CODE", code, + "RET_TYPE", hasRet ? block.locals[0].type.minimal : "", + "RET_NAME", hasRet ? block.locals[0].name : "", + "BND", bnd, + "CALL", kernel.callString() + (hasLocals ? ","+block.locals.names() : ""), + "METHOD", reduce ? "reduce" : "for", + "PRAGMA", "\n#pragma", + "NL", "\n", + "COMMA", ",", + "JOINER", joiner.str(), + "OMP_PRE", preReduce.str(), + "OMP_POST", ompPost, + "" }; + + // generate kernel + string templ = doubleKernel ? TmpDoubleKernel : TmpSingleKernel; + if (mtType == MTNone) + replaceAll(templ, "$RUN$", TmpRunSimple); + else if (mtType == MTTBB) + replaceAll(templ, "$RUN$", TmpRunTBB); + else if (mtType == MTOpenMP) { + string ompTempl = TmpRunOMP; + replaceAll(ompTempl, "$OMP_DIRECTIVE$", TmpOMPDirective); + replaceAll(templ, "$RUN$", ompTempl); + } + + // synthesize code + sink.inplace << block.linebreaks() << replaceSet(templ, table); + + // adjust lines after OMP block + if (mtType == MTOpenMP) + sink.inplace << "\n#line " << block.line1 << " \"" << sink.infile << "\"\n" << endl; +} diff --git a/source/blender/python/manta_full/source/preprocessor/codegen_python.cpp b/source/blender/python/manta_full/source/preprocessor/codegen_python.cpp new file mode 100644 index 00000000000..b00d1a15b7e --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/codegen_python.cpp @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor: Process replacement text of PYTHON keywords + * + ******************************************************************************/ + +#include "prep.h" +#include <cstdlib> +#include <set> +#include <sstream> +#include <iostream> +using namespace std; + +#define STR(x) #x + +//****************************************************** +// Templates for code generation + +const string TmpFunction = STR( +$TEMPLATE$ static PyObject* _W_$WRAPPER$ (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + pbPreparePlugin(parent, "$FUNCNAME$" ); + PyObject *_retval = 0; + { + ArgLocker _lock; + $ARGLOADER$ + @IF(RET_VOID) + _retval = getPyNone(); + $FUNCNAME$($CALLSTRING$); + @ELSE + _retval = toPy($FUNCNAME$($CALLSTRING$)); + @END + _args.check(); + } + pbFinalizePlugin(parent,"$FUNCNAME$" ); + return _retval; + } catch(std::exception& e) { + pbSetError("$FUNCNAME$",e.what()); + return 0; + } +} +); + +const string TmpMemberFunction = STR( +$TEMPLATE$ static PyObject* _W_$WRAPPER$ (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { + try { + PbArgs _args(_linargs, _kwds); + $CLASS$* pbo = dynamic_cast<$CLASS$*>(Pb::objFromPy(_self)); + pbPreparePlugin(pbo->getParent(), "$CLASS$::$FUNCNAME$"); + PyObject *_retval = 0; + { + ArgLocker _lock; + $ARGLOADER$ + pbo->_args.copy(_args); + @IF(RET_VOID) + _retval = getPyNone(); + pbo->$FUNCNAME$($CALLSTRING$); + @ELSE + _retval = toPy(pbo->$FUNCNAME$($CALLSTRING$)); + @END + pbo->_args.check(); + } + pbFinalizePlugin(pbo->getParent(),"$CLASS$::$FUNCNAME$"); + return _retval; + } catch(std::exception& e) { + pbSetError("$CLASS$::$FUNCNAME$",e.what()); + return 0; + } +}); + +const string TmpConstructor = STR( +$TEMPLATE$ static int _W_$WRAPPER$ (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { + PbClass* obj = Pb::objFromPy(_self); + if (obj) delete obj; + try { + PbArgs _args(_linargs, _kwds); + pbPreparePlugin(0, "$CLASS$::$FUNCNAME$" ); + { + ArgLocker _lock; + $ARGLOADER$ + obj = new $CLASS$($CALLSTRING$); + obj->registerObject(_self, &_args); + _args.check(); + } + pbFinalizePlugin(obj->getParent(),"$CLASS$::$FUNCNAME$" ); + return 0; + } catch(std::exception& e) { + pbSetError("$CLASS$::$FUNCNAME$",e.what()); + return -1; + } +}); + +const string TmpBinaryOp = STR( +$TEMPLATE$ static PyObject* _W_$WRAPPER$ (PyObject* _self, PyObject* o) { + try { + PbArgs _args(0,0); + _args.addLinArg(o); + $CLASS$* pbo = dynamic_cast<$CLASS$*>(Pb::objFromPy(_self)); + pbPreparePlugin(pbo->getParent(), "$CLASS$::$FUNCNAME$"); + PyObject *_retval = 0; + { + ArgLocker _lock; + $ARGLOADER$ + pbo->_args.copy(_args); + _retval = toPy(pbo->$FUNCNAME$($CALLSTRING$)); + pbo->_args.check(); + } + pbFinalizePlugin(pbo->getParent(),"$CLASS$::$FUNCNAME$"); + return _retval; + } catch(std::exception& e) { + pbSetError("$CLASS$::$FUNCNAME$",e.what()); + return 0; + } +}); + +const string TmpGetSet = STR( +static PyObject* _GET_$NAME$(PyObject* self, void* cl) { + $CLASS$* pbo = dynamic_cast<$CLASS$*>(Pb::objFromPy(self)); + return toPy(pbo->$NAME$); +} +static int _SET_$NAME$(PyObject* self, PyObject* val, void* cl) { + $CLASS$* pbo = dynamic_cast<$CLASS$*>(Pb::objFromPy(self)); + pbo->$NAME$ = fromPy<$TYPE$ >(val); + return 0; +}); + +const string TmpRegisterMethod = STR( +@IF(CTPL) + static const Pb::Register _R_$IDX$ ("$CLASS$<$CT$>","$FUNCNAME$",$CLASS$<$CT$>::_W_$REGWRAPPER$); +@ELIF(CLASS) + static const Pb::Register _R_$IDX$ ("$CLASS$","$FUNCNAME$",$CLASS$::_W_$REGWRAPPER$); +@ELSE + static const Pb::Register _RP_$FUNCNAME$ ("","$FUNCNAME$",_W_$REGWRAPPER$); +@END +); + +const string TmpRegisterGetSet = STR( +@IF(CTPL) + static const Pb::Register _R_$IDX$ ("$CLASS$<$CT$>$","$PYNAME$",$CLASS$<$CT$>::_GET_$NAME$,$CLASS$<$CT$>::_SET_$NAME$); +@ELSE + static const Pb::Register _R_$IDX$ ("$CLASS$","$PYNAME$",$CLASS$::_GET_$NAME$,$CLASS$::_SET_$NAME$); +@END +); + +const string TmpRegisterClass = STR( +@IF(CTPL) + static const Pb::Register _R_$IDX$ ("$CLASS$<$CT$>","$PYNAME$<$CT$>","$BASE$$BTPL$"); + template<> const char* Namify<$CLASS$<$CT$> >::S = "$CLASS$<$CT$>"; +@ELSE + static const Pb::Register _R_$IDX$ ("$CLASS$","$PYNAME$","$BASE$$BTPL$"); + template<> const char* Namify<$CLASS$ >::S = "$CLASS$"; +@END +); + +const string TmpAlias = STR( +static const Pb::Register _R_$IDX$ ("$CLASS$","$PYNAME$",""); +); + +const string TmpTemplateWrapper = STR( +static $RET$ _W_$WRAPPER$ (PyObject* s, PyObject* l, PyObject* kw) { + PbArgs args(l, kw); + int hits=0; + $RET$ (*call)(PyObject*,PyObject*,PyObject*); + + $TEMPLATE_CHECK$ + + if (hits == 1) + return call(s,l,kw); + if (hits == 0) + pbSetError("$FUNCNAME$", "Can't deduce template parameters"); + else + pbSetError("$FUNCNAME$", "Argument matches multiple templates"); + return @IF(CONSTRUCTOR) -1 @ELSE 0 @END ; +} +); +const string TmpTemplateWrapperOp = STR( +static PyObject* _W_$WRAPPER$ (PyObject* _self, PyObject* o) { + PbArgs args(0,0); + args.addLinArg(o); + int hits=0; + PyObject* (*call)(PyObject*,PyObject*); + + $TEMPLATE_CHECK$ + + if (hits == 1) + return call(_self, o); + if (hits == 0) + pbSetError("$FUNCNAME$", "Can't deduce template parameters"); + else + pbSetError("$FUNCNAME$", "Argument matches multiple templates"); + return 0; +} +); + +const string TmpTemplateChecker = STR( +template $TEMPLATE$ +static bool $NAME$ (PbArgs& A) { + return $CHK$; +} +); + +//****************************************************** +// Code generation functions + +string generateLoader(const Argument& arg) { + bool integral = isIntegral(arg.type.name); + Type ptrType = arg.type; + string optCall = string("_args.") + (arg.value.empty() ? "get" : "getOpt"); + + ptrType.isConst = false; + if (integral) { + ptrType.isPointer = false; + ptrType.isRef = false; + } else if (arg.type.isPointer) { + ptrType.isPointer = false; + ptrType.isRef = false; + optCall = string("_args.") + (arg.value.empty() ? "getPtr" : "getPtrOpt"); + } else if (arg.type.isRef) { + ptrType.isPointer = false; + ptrType.isRef = false; + optCall = string("*_args.") + (arg.value.empty() ? "getPtr" : "getPtrOpt"); + } + + stringstream loader; + loader << arg.type.build() << " " << arg.name << " = " << optCall; + + loader << "<" << ptrType.build() << " >"; + loader << "(\"" << arg.name << "\"," << arg.index << ","; + if (!arg.value.empty()) + loader << arg.value << ","; + loader << "&_lock); "; + + return loader.str(); +} + +// global for tracking state between python class and python function registrations +bool gFoundConstructor = false; + +void processPythonFunction(const Block& block, const string& code, Sink& sink, vector<Instantiation>& inst) { + const Function& func = block.func; + + // PYTHON(...) keyword options + for (size_t i=0; i<block.options.size(); i++) { + if (block.options[i].name == "only") { + ((Function&)func).name = makeSafe(func.name); + } + else + errMsg(block.line0, "unknown keyword " + block.options[i].name); + } + + bool isConstructor = func.returnType.minimal.empty(); + bool isPlugin = !block.parent; + bool doRegister = true; + string className = isPlugin ? "" : block.parent->name; + if (isConstructor) gFoundConstructor = true; + if (isPlugin && sink.isHeader) + errMsg(block.line0,"plugin python functions can't be defined in headers."); + + // replicate function + if (gDocMode) { + // document free plugins + if (isPlugin) + sink.inplace << "//! \\ingroup Plugins\n"; + sink.inplace << "PYTHON " << func.signature() << "{}\n"; + return; + } + sink.inplace << block.linebreaks() << func.signature() << block.initList << code; + + // generate variable loader + string loader = ""; + for (int i=0; i<(int)func.arguments.size(); i++) + loader += generateLoader(func.arguments[i]); + + // wrapper name + static int idx; + stringstream wrapper; + wrapper << idx++; + + // generate glue layer function + const string table[] = { "FUNCNAME", func.name, + "ARGLOADER", loader, + "TEMPLATE", func.isTemplated() ? "template "+func.templateTypes.minimal : "", + "WRAPPER", (func.isTemplated() ? "T_":"") + wrapper.str(), + "REGWRAPPER", wrapper.str(), + "CLASS", className, + "CTPL", (isPlugin || !block.parent->isTemplated()) ? "" : "$CT$", + "CALLSTRING", func.callString(), + "RET_VOID", (func.returnType.name=="void") ? "Y" : "", + "" }; + string callerTempl = isPlugin ? TmpFunction : TmpMemberFunction; + if (isConstructor) callerTempl = TmpConstructor; + else if (func.isOperator) callerTempl = TmpBinaryOp; + sink.inplace << replaceSet(callerTempl, table); + + // drop a marker for function template wrapper + if (func.isTemplated()) { + Instantiation* curInst = 0; + for (int i=0; i<(int)inst.size(); i++) + if (inst[i].cls == className && inst[i].name == func.name) curInst=&inst[i]; + if (!curInst) { + stringstream num; num << inst.size(); + sink.inplace << "$" << num.str() << "$"; + inst.push_back(Instantiation(className,func.name)); + curInst = &inst.back(); + } else + doRegister = false; + curInst->wrapName.push_back(wrapper.str()); + curInst->func.push_back(func); + } + + // register functions + if (!doRegister) return; + const string reg = replaceSet(TmpRegisterMethod, table); + if (isPlugin) { + sink.inplace << reg; + } else { + sink.link << '+' << block.parent->name << '^' << reg << '\n'; + } +} + +void processPythonVariable(const Block& block, Sink& sink) { + const Function& var = block.func; + + if (!block.parent) + errMsg(block.line0, "python variables can only be used inside classes"); + + // process options + string pythonName = var.name; + for (size_t i=0; i<block.options.size(); i++) { + if (block.options[i].name == "name") + pythonName = block.options[i].value; + else + errMsg(block.line0, "PYTHON(opt): illegal option. Supported options are: 'name'"); + } + + // generate glue layer function + const string table[] = { "NAME", var.name, + "CLASS", block.parent->name, + "CTPL", !block.parent->isTemplated() ? "" : "$CT$", + "PYNAME", pythonName, + "TYPE", var.returnType.minimal, + "" }; + + // output function and accessors + sink.inplace << block.linebreaks() << var.minimal << ";"; + sink.inplace << replaceSet(TmpGetSet, table); + + // register accessors + const string reg = replaceSet(TmpRegisterGetSet, table); + sink.link << '+' << block.parent->name << '^' << reg << '\n'; +} + +void processPythonClass(const Block& block, const string& code, Sink& sink, vector<Instantiation>& inst) { + const Class& cls = block.cls; + string pythonName = cls.name; + + if (!sink.isHeader) + errMsg(block.line0, "PYTHON classes can only be defined in header files."); + + // PYTHON(...) keyword options + for (size_t i=0; i<block.options.size(); i++) { + if (block.options[i].name == "name") + pythonName = block.options[i].value; + else + errMsg(block.line0, "PYTHON(opt): illegal kernel option. Supported options are: 'name'"); + } + + if (gDocMode) { + sink.inplace << "//! \\ingroup PyClasses\nPYTHON " << cls.minimal; + return; + } + + // register class + const string table[] = { "CLASS", cls.name, + "BASE", cls.baseClass.name, + "BTPL", cls.baseClass.isTemplated() ? "<$BT$>" : "", + "PYNAME", pythonName, + "CTPL", cls.isTemplated() ? "CT" : "", + "" }; + + // register class + string reg = replaceSet(TmpRegisterClass, table); + sink.link << '+' << cls.name << '^' << reg << '\n'; + // instantiate directly if not templated + if (!cls.isTemplated()) + sink.link << '>' << cls.name << "^\n"; + // chain the baseclass instantiation + if (cls.baseClass.isTemplated()) + sink.link << '@' << cls.name << '^' << cls.templateTypes.names() << '^' + << cls.baseClass.name << '^' << cls.baseClass.templateTypes.names() << '\n'; + + // write signature + sink.inplace << block.linebreaks() << cls.minimal << "{"; + + // remove first {, and steal two linebreaks so we can add a #define later + string ncode = code.substr(1); + stealLinebreaks(ncode, 2); + + // scan code for member functions + gFoundConstructor = false; + processText(ncode.substr(1), block.line0, sink, &cls, inst); + if (!gFoundConstructor) + errMsg(block.line0, "no PYTHON constructor found in class '" + cls.name + "'"); + + // add secret bonus members to class and close + sink.inplace << "public: PbArgs _args;"; + sink.inplace << "}\n"; + // add a define to make commenting out classes, and #ifdefs work correctly + sink.inplace << "#define _C_" << cls.name << '\n'; +} + +void processPythonInstantiation(const Block& block, const Type& aliasType, Sink& sink, vector<Instantiation>& inst) { + string parent = block.parent ? block.parent->name : ""; + // for template functions, add to instantiation list + bool isFunction = false; + for (int i=0; i<(int)inst.size(); i++) { + if (inst[i].cls == parent && inst[i].name == aliasType.name) { + inst[i].templates.push_back(aliasType.templateTypes.listText); + isFunction = true; + break; + } + } + // otherwise, assume it's a class, and put down a link-time instantiation request + if (!isFunction) { + sink.link << '>' << aliasType.name << '^' << aliasType.templateTypes.listText << '\n'; + } +} + +void processPythonAlias(const Block& block, const Type& aliasType, const string& aliasName, Sink& sink) { + const string table[] = {"CLASS", strip(aliasType.build()), "PYNAME", aliasName, ""}; + if (!aliasName.empty()) + sink.link << '&' << replaceSet(TmpAlias,table) << '\n'; +} + +// build the template argument checker needed for template deduction in the wrapper +string buildTemplateChecker(string& out, const Function& func) { + stringstream chk; + for (int k=0; k<(int)func.arguments.size(); k++) { + stringstream num; num << k; + Type type = func.arguments[k].type; + type.isPointer = false; + type.isRef = false; + type.isConst = false; + chk << "A.typeCheck<" << type.build() << " >(" + << num.str() << ",\"" << func.arguments[k].name << "\")"; + + if (k != (int)func.arguments.size()-1) + chk << " && "; + } + + static int idx = 0; + stringstream name; + name << "_K_" << idx++; + const string table[] = { "TEMPLATE", func.templateTypes.minimal, + "NAME", name.str(), + "CHK", chk.str(), + "" }; + out+= replaceSet(TmpTemplateChecker,table); + return name.str(); +} + +// add a wrapper for all templated function +void postProcessInstantiations(Sink& sink, vector<Instantiation>& inst) { + string out = sink.inplace.str(); + for (int i=0; i<(int)inst.size(); i++) { + Instantiation& cur = inst[i]; + if (cur.templates.size() == 0) + errMsg(0, cur.cls + "::" + cur.name + " : templated function without instantiation detected."); + + string wrapper = ""; + stringstream chkCall; + // loop over overloaded functions + for (int k=0; k<(int)cur.func.size(); k++) { + string chkFunc = buildTemplateChecker(wrapper, cur.func[k]); + + // build argument checker + for (int j=0; j<(int)cur.templates.size(); j++) { + stringstream num; num << j; + chkCall << "if (" << chkFunc << "<" << cur.templates[j] << ">(args)) {"; + chkCall << "hits++; call = _W_T_" << cur.wrapName[k] << "<" << cur.templates[j] <<">; }"; + } + } + const string table[] = { "CONSTRUCTOR", cur.name == cur.cls ? "Y":"", + "WRAPPER", cur.wrapName[0], + "TEMPLATE_CHECK", chkCall.str(), + "FUNCNAME", cur.name, + "RET", cur.name == cur.cls ? "int" : "PyObject*", + "" }; + wrapper += replaceSet(cur.func[0].isOperator ? TmpTemplateWrapperOp : TmpTemplateWrapper, table); + + stringstream num; + num << "$" << i << "$"; + replaceAll(out, num.str(), wrapper); + } + sink.inplace.str(out); +}
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/preprocessor/main.cpp b/source/blender/python/manta_full/source/preprocessor/main.cpp new file mode 100644 index 00000000000..02c0c3a0c03 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/main.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Main + * + ******************************************************************************/ + +#include <string> +#include <sstream> +#include <fstream> +#include <streambuf> +#include <iostream> +#include <cstdlib> +#include <cstring> + +#include "prep.h" + +using namespace std; + +string gFilename; +bool gDebugMode = true; +bool gDocMode; +bool gIsHeader; +MType gMTType = MTNone; + + +void usage() { + cerr << "preprocessor error: Unknown parameters." << endl; + cerr << " Usage : prep generate <dbg_mode> <mt_type> <inputdir> <inputfile> <outputfile>" << endl; + cerr << " or : prep docgen <dbg_mode> <mt_type> <inputdir> <inputfile> <outputfile>" << endl; + cerr << " or : prep link <regfiles...>" << endl; + exit(1); +} + +void doMerge(int argc, char* argv[]) { + if (argc < 3) usage(); + + generateMerge(argc-2, &argv[2]); +} + +void doGenerate(int argc, char* argv[], bool docs) { + gDocMode = docs; + gDebugMode = false; + gMTType = MTNone; + if (argc != 7) usage(); + + // set constants + const string indir(argv[4]), infile(argv[5]), outfile(argv[6]); + bool isPython = infile.size() > 3 && !infile.compare(infile.size()-3, 3, ".py"); + + // TP : only enable in cmake's PREP_DEBUG mode (passed via cmd line option dbg_mode) + gDebugMode = atoi(argv[2]) != 0; + if (!strcmp(argv[3],"TBB")) gMTType = MTTBB; + if (!strcmp(argv[3],"OPENMP")) gMTType = MTOpenMP; + + // load complete file into buffer + gFilename = indir+infile; + string text = readFile(gFilename); + if (text.empty()) { + cerr << "preprocessor error: Can't read file '" << infile << "'" << endl; + exit(1); + } + // pad text for easier lexing lookups + text += "\n\n\n"; + + Sink sink(infile,outfile); + if (gDocMode) { + sink.inplace << "/*! \\file " + infile + " */\n"; + } else { + sink.inplace << "\n\n\n\n\n// DO NOT EDIT !\n"; + sink.inplace << "// This file is generated using the MantaFlow preprocessor (prep generate)."; + sink.inplace << "\n\n\n\n\n"; + } + + if (isPython) { + // python file, only registering + replaceAll(text, "\n", "\\n"); + replaceAll(text, "\r", ""); + replaceAll(text, "\t", "\\t"); + replaceAll(text, "\"", "<qtm>"); // split into two ops to avoid interference + replaceAll(text, "<qtm>", "\\\""); + sink.link << "#include \"registry.h\"\n"; + sink.link << "static const Pb::Register _reg(\"" + infile + "\", \"" + text + "\");\n"; + } else { + if (!gDocMode) { + sink.link << "#include \"" + infile + "\"\n"; + if (!gDebugMode) + sink.inplace << "#line 1 \"" << indir << infile << "\"\n"; + } + std::vector<Instantiation> inst; + processText(text, 1, sink, 0, inst); + postProcessInstantiations(sink, inst); + } + sink.write(); +} + + +int main(int argc, char* argv[]) { + // command line options + if (argc < 2) usage(); + + // use merger + if (!strcmp(argv[1],"link")) + doMerge(argc, argv); + else if (!strcmp(argv[1],"generate")) + doGenerate(argc, argv, false); + else if (!strcmp(argv[1],"docgen")) + doGenerate(argc, argv, true); + else + usage(); + + return 0; +} + diff --git a/source/blender/python/manta_full/source/preprocessor/merge.cpp b/source/blender/python/manta_full/source/preprocessor/merge.cpp new file mode 100644 index 00000000000..ae103b76f38 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/merge.cpp @@ -0,0 +1,179 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor merge file gen + * + ******************************************************************************/ + +#include <map> +#include <iostream> +#include <sstream> +#include <cstdlib> +#include "prep.h" +using namespace std; + +struct Chain { + Chain() {}; + Chain(const string& tpl, const string& target, const string& targetTpl) : + tpl(tpl), target(target), targetTpl(targetTpl) {} + string tpl, target, targetTpl; +}; + +struct Request { + Request(const string& c, const string& t) : cls(c), tpl(t), base("") {} + string cls, tpl, base; +}; + +struct RegFile { + RegFile(const string& name) : filename(name),idx(0) {} + + string filename; + ostringstream out, header, footer; + vector<Request> req; + int idx; +}; + +struct ClassInfo { + map<string,bool> tplDone; + vector<string> snippets; +}; + +static map<string, ClassInfo> classes; +static map<string, Chain> chains; +static vector<RegFile *> regFiles; + +// compare template arguments +string mapArgs(const string& inst, const string& match, const string& target) { + vector<string> inArg = split(inst,','); + vector<string> maArg = split(match,','); + vector<string> taArg = split(target,','); + vector<string> destArg = taArg; + + for (int i=0; i<(int)maArg.size(); i++) { + for (int j=0; j<(int)taArg.size(); j++) { + if (maArg[i] == taArg[j]) { + destArg[j] = inArg[i]; + } + } + } + stringstream s; + for (int i=0; i<(int)destArg.size(); i++) { + s << destArg[i]; + if (i != (int)destArg.size()-1) s << ','; + } + return s.str(); +} + +void resolveChains(RegFile& file) { + for (int i=0; i<(int)file.req.size(); i++) { + Request& req = file.req[i]; + map<string, Chain>::iterator it = chains.find(req.cls); + if (it != chains.end()) { + Chain& chain = it->second; + string tpl = mapArgs(req.tpl, chain.tpl, chain.targetTpl); + req.base = tpl; + file.req.push_back(Request(chain.target, tpl)); + } + } +} + +void resolveRequests(RegFile& file) { + // sort request by class + map<string, vector<Request*> > sortedReqs; + for (int i=0; i<(int)file.req.size(); i++) { + Request& req = file.req[i]; + ClassInfo& info = classes[req.cls]; + if (!info.tplDone[req.tpl]) { + info.tplDone[req.tpl] = true; + sortedReqs[req.cls].push_back(&req); + } + } + + // process requests + for(map<string,vector<Request*> >::iterator it = sortedReqs.begin(); it != sortedReqs.end(); ++it) { + ClassInfo& info = classes[it->first]; + file.out << "#ifdef _C_" << it->first << '\n'; + for (int i=0; i<(int)it->second.size(); i++) { + Request& req = *(it->second[i]); + for (int j=0; j<(int)info.snippets.size(); j++) { + stringstream idxStr; + idxStr << file.idx++; + const string table[] = {"CT", req.tpl, "BT", req.base, "IDX", idxStr.str(), ""}; + file.out << replaceSet(info.snippets[j], table) << '\n'; + } + } + file.out << "#endif\n"; + } +} + +// create data structure from regfiles +void parseLine(const string& line, RegFile& file) { + if (line.empty()) return; + + vector<string> parts = split(line,'^'); + string cls = parts[0].substr(1); + + if (line[0] == '+') + classes[cls].snippets.push_back(parts[1]); + else if (line[0] == '>') + file.req.push_back(Request(cls,parts[1])); + else if (line[0] == '@') + chains[cls] = Chain(parts[1],parts[2],parts[3]); + else if (line[0] == '#') + file.header << line << '\n'; + else if (line[0] == '&') { + string txt = line.substr(1); + stringstream num; num << file.idx++; + replaceAll(txt, "$IDX$", num.str()); + file.footer << txt << '\n'; + } else { + file.out << line << '\n'; + } +} + +void generateMerge(int num, char* files[]) { + // parse files + for (int i=0; i<num; i++) { + regFiles.push_back(new RegFile(files[i])); + + string text = readFile(files[i]); + replaceAll(text,"\r",""); + vector<string> lines = split(text,'\n'); + + for (int j=0; j<(int)lines.size(); j++) + parseLine(lines[j], *regFiles.back()); + } + + // process and save files + for (int i=0; i<num; i++) { + resolveChains(*regFiles[i]); + resolveRequests(*regFiles[i]); + + string text = "", fn = regFiles[i]->filename; + bool isPython = fn.compare(fn.size()-7, 7, ".py.reg") == 0; + + if (regFiles[i]->idx > 0) { + text = "\n\n\n\n\n// DO NOT EDIT !\n"; + text += "// This file is generated using the MantaFlow preprocessor (prep link)."; + text += "\n\n\n\n\n"; + text += regFiles[i]->header.str(); + text += "namespace Manta {\n"; + text += regFiles[i]->out.str(); + text += regFiles[i]->footer.str(); + text += "}"; + } else if (isPython) { + text = regFiles[i]->header.str() + regFiles[i]->out.str() + regFiles[i]->footer.str(); + } + string filename = fn + ".cpp"; + // only write if content is different + if (!fileExists(filename) || readFile(filename) != text) + writeFile(filename, text); + delete regFiles[i]; + } +} diff --git a/source/blender/python/manta_full/source/preprocessor/parse.cpp b/source/blender/python/manta_full/source/preprocessor/parse.cpp new file mode 100644 index 00000000000..8af4e33f2b6 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/parse.cpp @@ -0,0 +1,356 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Parsing + * + ******************************************************************************/ + +#include <iostream> +#include <cstdlib> +#include <algorithm> +#include "prep.h" + +using namespace std; + +List<Type> parseTypeList(TokenPointer& parentPtr); + +//************************************************************* +// parsers + +#define tkAssert(x,msg) {if(!(x)){tk.errorMsg(msg);}} +#define typeAssert(x) tkAssert(x," is not a valid type.") +#define argAssert(x) tkAssert(x," is not a valid argument.") + +string parseRunaway(TokenPointer& parentPtr) { + Text text; + TokenPointer tk(parentPtr, &text); + + // don't validate, just track bracket level + BracketStack stack; + for(;!tk.done();tk.next()) { + if (stack.empty() && (tk.curType() == TkComma || + tk.curType() == TkBracketR || tk.curType() == TkTBracketR)) { + break; + } + if (tk.curType() == TkBracketL || tk.curType() == TkTBracketL) { + stack.push_back(tk.cur().text[0]); + } else if (tk.curType() == TkBracketR) { + argAssert(stack.pop() == '('); + } else if (tk.curType() == TkTBracketR) { + argAssert(stack.pop() == '<'); + } + } + argAssert(stack.empty()); + return text.minimal; +} + +void parsePointer(TokenPointer& tk, Type& cur) { + if (tk.done()) return; + if (tk.cur().type == TkOperator && tk.cur().text == "*") { + cur.isPointer = true; + tk.next(); + } else if (tk.cur().type == TkRef) { + cur.isRef = true; + tk.next(); + } +} + +Type parseType(TokenPointer& parentPtr) { + Type cur = Type(); + TokenPointer tk(parentPtr, &cur); + + // constness + if (tk.curType() == TkConst) { + cur.isConst = true; + tk.next(); + } + typeAssert(!tk.done()); + + // signed / unsigned + if (tk.curType() == TkTypeQualifier) { + cur.name = tk.cur().text; + tk.next(); + if (tk.done()) + return cur; + typeAssert(tk.curType() == TkSimpleType); + cur.name += " " + tk.cur().text; + tk.next(); + parsePointer(tk, cur); + return cur; + } + + // template argument + if (tk.curType() == TkClass) { + tk.next(); + } + + typeAssert(tk.curType() == TkDescriptor || tk.curType() == TkSimpleType); + cur.name = tk.cur().text; + tk.next(); + if (cur.name == "operator") { + while (tk.curType() == TkOperator) { + cur.name += tk.cur().text; + tk.next(); + } + } + + // namespace + if (tk.curType() == TkDoubleColon) { + cur.name += "::"; + tk.next(); + typeAssert(tk.curType() == TkDescriptor || tk.curType() == TkSimpleType); + cur.name += tk.cur().text; + tk.next(); + } + + // template + if (tk.curType() == TkTBracketL) { + cur.templateTypes = parseTypeList(tk); + } + + parsePointer(tk, cur); + return cur; +} + +Argument parseArgument(TokenPointer& parentPtr, bool requireName, bool requireType) { + Argument cur = Argument(); + TokenPointer tk(parentPtr, &cur); + + if (requireType) + cur.type = parseType(tk); + + if (tk.curType() != TkDescriptor && !requireName) + return cur; + + argAssert(tk.curType() == TkDescriptor); + cur.name = tk.cur().text; + tk.next(); + + // default value ? + if (tk.curType() == TkBracketL || (tk.curType() == TkOperator && tk.cur().text == "=")) { + if (tk.curType() == TkOperator) + tk.next(); + cur.value = parseRunaway(tk); + } + return cur; +} + +List<Type> parseTypeList(TokenPointer& parentPtr) { + List<Type> list; + TokenPointer tk(parentPtr, &list); + + tkAssert(tk.curType() == TkTBracketL, "expect template opening bracket"); + tk.next(); + if (tk.curType() != TkTBracketR) { + for(;;) { + list.push_back(parseType(tk)); + if (tk.curType() == TkTBracketR) + break; + tkAssert(tk.curType() == TkComma, "expect comma or closing bracket"); + tk.next(); + } + } + list.listText = list.minimal.substr(1); + tkAssert(tk.curType() == TkTBracketR, "expect template closing bracket"); + tk.next(); + return list; +} + +List<Argument> parseArgumentList(TokenPointer& parentPtr, bool requireName, bool requireType) { + List<Argument> list; + TokenPointer tk(parentPtr, &list); + + tkAssert(tk.curType() == TkBracketL, "expect opening bracket"); + tk.next(); + if (tk.curType() != TkBracketR) { + for(int idx=0;;idx++) { + list.push_back(parseArgument(tk, requireName, requireType)); + list.back().index = idx; + if (tk.curType() == TkBracketR) + break; + tkAssert(tk.curType() == TkComma, "expect comma or closing bracket"); + tk.next(); + } + } + list.listText = list.minimal.substr(1); + tkAssert(tk.curType() == TkBracketR, "expect closing bracket"); + tk.next(); + return list; +} + +Function parseFunction(TokenPointer& parentPtr, bool requireNames, bool requireType, bool requireArgs) { + Function cur; + TokenPointer tk(parentPtr, &cur); + + // templated + if (tk.curType() == TkTemplate) { + tk.next(); + cur.templateTypes = parseTypeList(tk); + } + + for (;tk.curType() == TkInline || tk.curType() == TkVirtual; tk.next()) { + if (tk.curType() == TkInline) cur.isInline = true; + if (tk.curType() == TkVirtual) cur.isVirtual = true; + } + + if (requireType) + cur.returnType = parseType(tk); + tkAssert(tk.curType() == TkDescriptor, "malformed function/kernel"); + cur.name = tk.cur().text; + tk.next(); + + if (cur.name == "operator") { + cur.isOperator = true; + while(tk.curType() == TkOperator) { + cur.name += tk.cur().text; + tk.next(); + } + } + + if (requireArgs || tk.curType() == TkBracketL) + cur.arguments = parseArgumentList(tk, requireNames, true); + else + cur.noParentheses = true; + + if (tk.curType() == TkConst) { + cur.isConst = true; + tk.next(); + } + return cur; +} + +Class parseClass(TokenPointer& parentPtr) { + Class cur; + TokenPointer tk(parentPtr, &cur); + + tkAssert(tk.curType() == TkClass, ""); + tk.next(); + tkAssert(tk.curType() == TkDescriptor, "malformed preprocessor keyword block. Expected 'PYTHON class name : public X {}'"); + cur.name = tk.cur().text; + tk.next(); + tkAssert(tk.curType() == TkColon, "PYTHON class must publicly derive from PbClass (or a subclass)"); + tk.next(); + tkAssert(tk.curType() == TkPublic, "PYTHON class must publicly derive from PbClass (or a subclass)"); + tk.next(); + tkAssert(tk.curType() == TkDescriptor, "PYTHON class must publicly derive from PbClass (or a subclass)"); + cur.baseClass = parseType(tk); + + return cur; +} + +// Parse syntax KEYWORD(opt1, opt2, ...) STATEMENTS [ {} or ; ] +void parseBlock(const string& kw, const vector<Token>& tokens, const Class* parent, Sink& sink, vector<Instantiation>& inst) { + Block block = Block(); + block.parent = parent; + TokenPointer tk(tokens, &block); + + // parse keyword options + if (tk.curType() == TkBracketL) + block.options = parseArgumentList(tk, true, false); + + if (kw == "KERNEL") { + List<Type> templTypes; + + // templated kernel + if (tk.curType() == TkTemplate) { + tk.next(); + templTypes = parseTypeList(tk); + } + + // return values + while (tk.curType() == TkDescriptor && tk.cur().text == "returns") { + tk.next(); + tkAssert(tk.curType() == TkBracketL, "expext opening bracket"); + tk.next(); + block.locals.push_back(parseArgument(tk, true, true)); + tkAssert(tk.curType() == TkBracketR, "expect closing bracket"); + tk.next(); + } + + block.func = parseFunction(tk, true, true, true); + if (!templTypes.empty()) + block.func.templateTypes = templTypes; + + tkAssert(tk.curType() == TkCodeBlock && tk.isLast(), + "Malformed KERNEL, expected KERNEL(opts...) ret_type name(args...) { code }"); + + block.line1 = tk.cur().line; + processKernel(block, tk.cur().text, sink); + } + else if (kw == "PYTHON") + { + // template instantiation / alias + if (tk.curType() == TkDescriptor && (tk.cur().text == "alias" || tk.cur().text == "instantiate")) { + string kw = tk.cur().text; + Type aliasType; + do { + tk.next(); + aliasType = parseType(tk); + processPythonInstantiation(block, aliasType, sink, inst); + } while (tk.curType() == TkComma); + + if (kw == "alias") { + tkAssert(tk.curType() == TkDescriptor, "malformed preprocessor block. Expected 'PYTHON alias cname pyname;'"); + string aliasName = tk.cur().text; + processPythonAlias(block, aliasType, aliasName, sink); + tk.next(); + } + tkAssert(tk.curType() == TkSemicolon && tk.isLast(), "malformed preprocessor block. Expected 'PYTHON alias/instantiate cname [pyname];'"); + sink.inplace << block.linebreaks(); + + return; + } + List<Type> templTypes; + + // resolve template class + Text templText; + if (tk.curType() == TkTemplate) { + TokenPointer t2(tk, &templText); + t2.next(); + templTypes = parseTypeList(t2); + } + + // python class + if (tk.curType() == TkClass && tk.cur().text != "typename") { + block.cls = parseClass(tk); + block.cls.templateTypes = templTypes; + block.cls.prequel(&templText); + tkAssert(tk.curType() == TkCodeBlock && tk.isLast(), "malformed preprocessor keyword block. Expected 'PYTHON class name : public X {}'"); + processPythonClass(block, tk.cur().text, sink, inst); + } + else // function or member + { + bool isConstructor = parent && tk.curType() == TkDescriptor && + parent->name == tk.cur().text && tk.previewType() == TkBracketL; + block.func = parseFunction(tk, false, !isConstructor, false); + block.func.templateTypes = templTypes; + block.func.prequel(&templText); + + if (isConstructor && tk.curType() == TkColon) { + // read till end + while(!tk.done() && tk.curType() != TkSemicolon && tk.curType() != TkCodeBlock) { + block.initList += tk.cur().text; + tk.next(); + } + tkAssert(!tk.done(), "Constructor initializer list not limited"); + } + + if (tk.curType() == TkSemicolon && block.func.noParentheses) { + tkAssert(tk.curType() == TkSemicolon && tk.isLast(), + "malformed preprocessor keyword block. Expected 'PYTHON type varname;'"); + processPythonVariable(block, sink); + } else { + tkAssert((tk.curType() == TkCodeBlock || tk.curType() == TkSemicolon) && tk.isLast(), + "malformed preprocessor keyword block. Expected 'PYTHON type funcname(args) [{}|;]'"); + processPythonFunction(block, tk.cur().text, sink, inst); + } + } + + } +} diff --git a/source/blender/python/manta_full/source/preprocessor/prep.h b/source/blender/python/manta_full/source/preprocessor/prep.h new file mode 100644 index 00000000000..f066b4fae65 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/prep.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Declarations + * + ******************************************************************************/ + +#ifndef _PREP_H +#define _PREP_H + +#include "util.h" +#include "code.h" +#include "tokenize.h" + +// from main.cpp +enum MType { MTNone = 0, MTTBB, MTOpenMP}; +extern std::string gFilename; +extern bool gDebugMode; +extern MType gMTType; +extern bool gDocMode; + +// functions from merge.cpp +void generateMerge(int num, char* files[]); + +// functions from tokenize.cpp +void processText(const std::string& text, int baseline, Sink& sink, const Class *parent, std::vector<Instantiation>& inst); + +// functions from parse.cpp +void parseBlock(const std::string& kw, const std::vector<Token>& tokens, const Class *parent, Sink& sink, std::vector<Instantiation>& inst); + +// functions from codegen_XXX.cpp +void processKernel(const Block& block, const std::string& code, Sink& sink); +void processPythonFunction(const Block& block, const std::string& code, Sink& sink, std::vector<Instantiation>& inst); +void processPythonVariable(const Block& block, Sink& sink); +void processPythonClass(const Block& block, const std::string& code, Sink& sink, std::vector<Instantiation>& inst); +void processPythonInstantiation(const Block& block, const Type& aliasType, Sink& sink, std::vector<Instantiation>& inst); +void processPythonAlias(const Block& block, const Type& aliasType, const std::string& aliasName, Sink& sink); +void postProcessInstantiations(Sink& sink, std::vector<Instantiation>& inst); + +#endif // _PREP_H diff --git a/source/blender/python/manta_full/source/preprocessor/tokenize.cpp b/source/blender/python/manta_full/source/preprocessor/tokenize.cpp new file mode 100644 index 00000000000..ae5294b7446 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/tokenize.cpp @@ -0,0 +1,348 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Tokenizer + * + ******************************************************************************/ + +#include <string> +#include <iostream> +#include <vector> +#include <algorithm> + +#include "prep.h" + +using namespace std; + +//************************************************************************************* +// TokenPointer class members + +TokenType TokenPointer::previewType() { + for (int i=ptr+1; i < (int)queue.size(); i++) { + if (queue[i].type != TkWhitespace && queue[i].type != TkComment) + return queue[i].type; + } + return TkNone; +} + +void TokenPointer::consumeWhitespace() { + if (done()) return; + while (cur().type == TkWhitespace || cur().type == TkComment) { + string minimal = (!txt->minimal.empty() && *(txt->minimal).rbegin() == ' ') ? "" : " "; + forward(minimal,cur().text,1); + if (ptr >= (int)queue.size()) + errMsg(-1, "Preprocessor ran out of tokens. This shouldn't happen."); + } +} + +void TokenPointer::next() { + forward(cur().text, cur().text, 1); + consumeWhitespace(); +} + +void TokenPointer::forward(const string& minimal, const string& original, int offset) { + txt->minimal += minimal; + txt->original += original; + ptr += offset; + if (parent) + parent->forward(minimal, original, offset); +} + +string TokenPointer::backtrace() { + string bt = "[" + txt->dynamicClass() + "]: " + txt->original + "\n"; + if (parent) + bt += parent->backtrace(); + return bt; +} + +void TokenPointer::errorMsg(const string& msg) { + string emsg = "'" + txt->original; + if (!done()) emsg += cur().text; + emsg += "' " + msg + "\n"; + emsg += " Preprocessor backtrace:\n" + backtrace(); + + int line = -1; + if (!queue.empty()) + line = done() ? queue.back().line : cur().line; + + errMsg(line, emsg); +} + +//************************************************************************************* +// Lexing functions + +// tokenize and parse until keyword section ends +void tokenizeBlock(vector<Token>& tokens, const string& kw, const string& text, size_t& i, int& line) { + tokens.push_back(Token(TkWhitespace, line)); + BracketStack brackets; + + // tokenize loop + bool comment=false, slComment=false, define=false, extendLine=false; + bool isString=false; + int codeblockLevel = 0; + for (; i<text.size(); i++) { + char c = text[i]; + if (c=='\r') continue; + + string& curstr = tokens.back().text; + bool lastchar = (i == text.size()-1) || (i == text.size()-2 && text[i+1]=='\r'); + + // track lines + bool isEOL = !extendLine && c=='\n'; + if (c=='\\') + extendLine = true; + else if (c!='\n') + extendLine = false; + if (c=='\n') line++; + + // track comments and defines + if (comment) { + if (!lastchar && c=='*' && text[i+1]=='/') comment = false; + curstr += c; + } + else if (slComment) { + if (isEOL) slComment = false; + curstr += c; + } + else if (define) { + if (isEOL) define = false; + curstr += c; + } + else if (!lastchar && c=='/' && text[i+1]=='*') { + comment = true; + if (codeblockLevel==0) + tokens.push_back(Token(TkComment, line, c)); + else + curstr += c; + } + else if (!lastchar && c=='/' && text[i+1]=='/') { + slComment = true; + if (codeblockLevel==0) + tokens.push_back(Token(TkComment, line, c)); + else + curstr += c; + } + else if (!isString && c=='\"' && codeblockLevel == 0) { + isString = true; + tokens.push_back(Token(TkString, line, c)); + } + else if (isString) { + if (c=='\"') isString = false; + curstr += c; + } + else if (c=='#') { + define = true; + if (codeblockLevel==0) + tokens.push_back(Token(TkComment, line, c)); + else + curstr += c; + } + + // track codeblock + else if (codeblockLevel > 0) { + curstr += c; + if (c == '}') codeblockLevel--; + if (c == '{') codeblockLevel++; + + if (codeblockLevel == 0) { + // block finished, return to parseText + return; + } + } + else if (c=='{') { + codeblockLevel++; + if (!brackets.empty()) + errMsg(line, "codeblock {} is not allowed inside brackets."); + tokens.push_back(Token(TkCodeBlock, line, c)); + } + + // track brackets + else if (c=='(') { + tokens.push_back(Token(TkBracketL, line, c)); + brackets.push_back(c); + } + else if (c==')') { + if (brackets.pop() != '(') + errMsg(line, "bracket mismatch, can't close ')'"); + tokens.push_back(Token(TkBracketR, line, c)); + } + else if (c=='<') { + tokens.push_back(Token(TkTBracketL, line, c)); + brackets.push_back(c); + } + else if (c=='>') { + if (brackets.pop() != '<') + errMsg(line, "bracket mismatch, can't close '>'"); + tokens.push_back(Token(TkTBracketR, line, c)); + } + + // track symbol tokens + else if (c==' ' || c=='\t' || c=='\n') { + tokens.push_back(Token(TkWhitespace, line, c)); + } + else if (c==',') { + tokens.push_back(Token(TkComma, line, c)); + } + else if (c=='*' || c=='-' || c=='+' || c=='/' || c=='=') { + tokens.push_back(Token(TkOperator, line, c)); + } + else if (c=='&') { + tokens.push_back(Token(TkRef, line, c)); + } + else if (c == ':') { + if (!lastchar && text[i+1]==':') { + tokens.push_back(Token(TkDoubleColon, line)); + tokens.back().text = "::"; + i++; + } else { + tokens.push_back(Token(TkColon, line, c)); + } + } + else if (c==';') { + if (!brackets.empty()) + errMsg(line, "Semicolon is not allowed inside brackets."); + + tokens.push_back(Token(TkSemicolon, line, c)); + + // block finished, return to parseText + return; + } + + // track descriptors + else if (isNameChar(c)) { + if (tokens.back().type != TkDescriptor) + tokens.push_back(Token(TkDescriptor, line, c)); + else + curstr += c; + } + // misc stuff: don't bother with more complex tokens + else { + if (tokens.back().type != TkNone) { + tokens.push_back(Token(TkNone, line, c)); + } else + curstr += c; + } + } + + // EOF before block end ? + errMsg(line, "EOF before block for keyword '" + kw + "' was closed."); + + return; +} + +void convertKeywords(vector<Token>& tokens) { + for (size_t i=0; i<tokens.size(); i++) { + if (tokens[i].type == TkDescriptor) { + const string& t = tokens[i].text; + if (t == "const") + tokens[i].type = TkConst; + else if (t == "unsigned" || t == "signed") + tokens[i].type = TkTypeQualifier; + else if (t == "char" || t == "int" || t == "short" || t == "long" || + t == "float" || t == "double" || t == "string" || t == "bool" || + t == "Vec3" || t == "Vec3i") + tokens[i].type = TkSimpleType; + else if (t == "PYTHON" || t == "KERNEL") + tokens[i].type = TkManta; + else if (t == "struct" || t == "class" || t == "typename") + tokens[i].type = TkClass; + else if (t == "inline") + tokens[i].type = TkInline; + else if (t == "public") + tokens[i].type = TkPublic; + else if (t == "virtual") + tokens[i].type = TkVirtual; + else if (t == "static") + tokens[i].type = TkStatic; + else if (t == "template") + tokens[i].type = TkTemplate; + else { + for (int i=0; !unsupportedKeywords[i].empty(); i++) { + if (t == unsupportedKeywords[i]) + tokens[i].type = TkUnsupportedKW; + } + } + } + } +} + +// parse complete file. +// Defer parts to be processed to processTextBlock, and directly copy the rest +void processText(const string& text, int baseline, Sink& sink, const Class* parent, vector<Instantiation>& inst) { + ostream& newText = sink.inplace; + + // no real lexing yet, only track define and comment blocks + string word = ""; + int line=baseline; + bool comment=false, slComment=false, define=false, extendLine=false; + for (size_t i=0; i<text.size()-1; i++) { + char c = text[i]; + if (c == '\r') continue; + + // track lines + bool isEOL = !extendLine && c=='\n'; + if (c=='\\') + extendLine = true; + else if (c!='\n') + extendLine = false; + if (c=='\n') line++; + + // track comments and defines + if (comment) { + if (c=='*' && text[i+1]=='/') comment = false; + newText << c; + } + else if (slComment) { + if (isEOL) slComment = false; + newText << c; + } + else if (define) { + if (isEOL) define = false; + newText << c; + } + else if (c=='/' && text[i+1]=='*') { + comment = true; + newText << word; + word = ""; + newText << c; + } + else if (c=='/' && text[i+1]=='/') { + slComment = true; + newText << word; + word = ""; + newText << c; + } + else if (c=='#') { + define = true; + newText << word; + word = ""; + newText << c; + } + else { + // tokenize keywords + if (isNameChar(c)) + word += c; + else { + if (word == "KERNEL" || word == "PYTHON") { + vector<Token> tokens; + tokenizeBlock(tokens, word, text, i, line); + convertKeywords(tokens); + parseBlock(word, tokens, parent, sink, inst); + } else { + newText << word; + newText << c; + } + word = ""; + } + } + } + newText << word; +} + + diff --git a/source/blender/python/manta_full/source/preprocessor/tokenize.h b/source/blender/python/manta_full/source/preprocessor/tokenize.h new file mode 100644 index 00000000000..8785c211bcd --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/tokenize.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Declarations + * + ******************************************************************************/ + +#ifndef _P_TOKENIZE_H +#define _P_TOKENIZE_H + +#include <string> +#include <vector> +#include "code.h" + +enum TokenType { TkNone = 0, TkComment, TkWhitespace, TkCodeBlock, TkDescriptor, TkComma, TkBracketL, + TkBracketR, TkTBracketL, TkTBracketR, TkPointer, TkRef, TkDoubleColon, TkSemicolon, + TkSimpleType, TkTypeQualifier, TkConst, TkEnd, TkManta, TkUnsupportedKW, TkClass, + TkInline, TkTemplate, TkStatic, TkVirtual, TkString, TkPublic, TkColon, TkOperator }; + +const std::string unsupportedKeywords[] = {"and", "and", "and_eq", "auto", "bitand", "bitor", "break", + "catch", "const_cast", "continue", "default", "delete", "do", "dynamic_cast", "else", "enum", + "explicit", "export", "extern", "for", "friend", "goto", "if", "mutable", "namespace", "new", + "not", "not_eq", "or", "or_eq", "private", "protected", "register", + "reinterpret_cast", "return", "sizeof", "static_cast", "switch", "this", "throw", "try", "typedef", + "union", "using", "volatile", "while", "xor", "xor_eq", "" }; + +inline bool isIntegral(const std::string& t) { + return t=="int" || t=="char" || t=="unsigned" || t=="bool" || t=="float" || t=="long" || + t=="double" || t=="Real" || t=="Vec3" || t=="Vec3i" || t=="string" || t=="std::string" || + t=="PbType" || t=="PbTypeVec"; +} + +struct Token { + Token(TokenType t, int l) : type(t), text(""), line(l) {} + Token(TokenType t, int l, char c) : type(t), text(""), line(l) { text += c; } + TokenType type; + std::string text; + int line; +}; + +// tracks a set of tokens, and the current position in this list +struct TokenPointer { + TokenPointer(const std::vector<Token>& t, Text *track) : parent(0),queue(t),ptr(0),txt(track) { reset(); } + TokenPointer(TokenPointer& t, Text *track) : parent(&t),queue(t.queue),ptr(t.ptr),txt(track) { reset(); } + TokenPointer *parent; + const std::vector<Token>& queue; + int ptr; + Text *txt; + + inline void reset() { txt->reset(); consumeWhitespace(); if(!done()) txt->line0 = cur().line; } + inline TokenType curType() { return done() ? TkEnd : cur().type; } + TokenType previewType(); + inline const Token& cur() { return queue[ptr]; } + inline bool done() { return ptr >= (int)queue.size(); } + inline bool isLast() { return ptr == (int)queue.size()-1;} + void forward(const std::string& minimal, const std::string& original, int offset); + void next(); + void consumeWhitespace(); + void errorMsg(const std::string& msg); + std::string backtrace(); +}; + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/preprocessor/util.cpp b/source/blender/python/manta_full/source/preprocessor/util.cpp new file mode 100644 index 00000000000..ae67f0e4b26 --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/util.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor merge file gen + * + ******************************************************************************/ + +#include <iostream> +#include <fstream> +#include <cstdlib> +#include <algorithm> +#include <stack> +#include "prep.h" +using namespace std; + +void errMsg(int line, const string& text) { + cerr << gFilename << ":" << line << ": error: " << text << endl; + exit(1); +} + +void debMsgHelper(int line, const string& text) { + cout << gFilename << ":" << line << ": info: " << text << endl; +} + +bool fileExists(const string& name) { + ifstream t(name.c_str()); + if (!t.good()) return false; + t.close(); + return true; +} + +string readFile(const string& name) { + ifstream t(name.c_str()); + if (!t.good()) return ""; + + string str; + + t.seekg(0, ios::end); + str.reserve((unsigned)t.tellg()); + t.seekg(0, ios::beg); + + str.assign((istreambuf_iterator<char>(t)), + istreambuf_iterator<char>()); + + t.close(); + return str; +} + +void writeFile(const string& name, const string& text) { + ofstream ofs(name.c_str(), ios::binary | ios::out); + if (!ofs.good()) { + cerr << "preprocessor error: Can't write to file '" << name << "'" << endl; + exit(1); + } + ofs << text; + ofs.close(); +} + +Sink::Sink(const string& infile, const string& outfile): + infile(infile), filename(outfile) +{ + isHeader = outfile.compare(outfile.size()-3, 3, ".py") == 0 || + outfile.compare(outfile.size()-2, 2, ".h") == 0; +} + +void Sink::write() { + writeFile(filename, inplace.str()); + if (isHeader && !gDocMode) { + writeFile(filename + ".reg", link.str()); + } +} + +vector<string> split(const string& text, char sep) { + vector<string> bins; + string cur; + for (int i=0; i<(int)text.size(); i++) { + if (text[i]==sep) { + bins.push_back(cur); + cur = ""; + } else + cur += text[i]; + } + bins.push_back(cur); + return bins; +} + +string strip(const string& s0) { + string s = s0; + while (s[0] == ' ') s=s.substr(1); + while (s[s.size()-1] == ' ') s=s.substr(0,s.size()-1); + return s; +} + +void replaceAll(string& source, string const& find, string const& replace) +{ + for(string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;) + { + source.replace(i, find.length(), replace); + i += replace.length() - find.length() + 1; + } +} + +string makeSafe(const string& s) { + string t="_X_"; + string source = "+=-<>!()"; + string trans = "12345678"; + for (int i=0; i<(int)s.size(); i++) { + size_t idx = source.find(s[i]); + t += (idx == string::npos) ? s[i] : trans[idx]; + } + return t; +} + +void stealLinebreaks(string& code, int num) { + // list all line breaks + vector<int> lb; + lb.push_back(-1); + for (int i=0; i<(int)code.size(); i++) + if (code[i] == '\n') + lb.push_back(i); + lb.push_back(code.size()); + + for (int i=lb.size()-2; i>0; i--) { + // make sure we don't mess with comments and defines + string curLine = code.substr(lb[i-1]+1,lb[i]-lb[i-1]-1); + string nextLine = code.substr(lb[i]+1, lb[i+1]-lb[i]-1); + if (curLine.find("//") == string::npos && + curLine.find('#') == string::npos && + nextLine.find('#') == string::npos) { + code[lb[i]] = ' '; + if (--num == 0) + return; + } + } + return; +} + +// Helpers for replaceSet +static string getBracketArg(const string& a, int &pos) { + string ret=""; + pos++; + for (;pos<(int)a.size(); pos++) { + if (a[pos]!='(' && a[pos]!=' ' && a[pos]!='$') break; + } + for (; pos<(int)a.size(); pos++) { + if (a[pos]==')' || a[pos]=='$') return ret; + ret += a[pos]; + } + return ""; +} + +inline bool compareKW(const string& a, int& pos, const string& kw) { + if (a.compare(pos+1,kw.size(),kw) == 0) { + pos += kw.size() ; + return true; + } + return false; +} + +string replaceSet(const string& templ, const string table[]) { + vector<string> key, value; + for (int i=0;!table[i].empty();i+=2) { + key.push_back(table[i]); + value.push_back(table[i+1]); + } + vector<bool> conditionStack; + conditionStack.push_back(true); + stringstream s; + int elifs = 0; + for (int i=0; i<(int)templ.size(); i++) { + char c = templ[i]; + if (c=='@') { + if (compareKW(templ,i,"IF")) { + string cond = getBracketArg(templ,i); + vector<string>::iterator it = find(key.begin(),key.end(),cond); + bool res = (it != key.end()) && (!value[it-key.begin()].empty()); + conditionStack.push_back(res); + } else if (compareKW(templ,i,"ELIF")) { + conditionStack.back() = !conditionStack.back(); + string cond = getBracketArg(templ,i); + vector<string>::iterator it = find(key.begin(),key.end(),cond); + bool res = (it != key.end()) && (!value[it-key.begin()].empty()); + conditionStack.push_back(res); + elifs++; + } else if (compareKW(templ,i,"END")) { + for (int k=0; k<elifs+1; k++) + conditionStack.pop_back(); + } else if (compareKW(templ,i,"ELSE")) { + conditionStack.back() = !conditionStack.back(); + } + continue; + } + // check condition + bool valid = true; + for (int k=0; k<(int)conditionStack.size(); k++) + if (!conditionStack[k]) valid = false; + if (!valid) continue; + + if (c=='$') { + string kw = getBracketArg(templ,i); + vector<string>::iterator it = find(key.begin(),key.end(),kw); + if (it == key.end()) + s << '$' << kw << '$'; + else + s << value[it - key.begin()]; + continue; + } + s << templ[i]; + } + return s.str(); +}
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/preprocessor/util.h b/source/blender/python/manta_full/source/preprocessor/util.h new file mode 100644 index 00000000000..e7b13726b8a --- /dev/null +++ b/source/blender/python/manta_full/source/preprocessor/util.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Preprocessor Declarations + * + ******************************************************************************/ + +#ifndef _P_UTIL_H +#define _P_UTIL_H + +#include <string> +#include <sstream> +#include <vector> + +// warnings, errors +void errMsg(int line, const std::string& text); + +// text tools +void replaceAll(std::string& text, const std::string& pattern, const std::string& repl); +std::string replaceSet(const std::string& templ, const std::string table[]); +std::vector<std::string> split(const std::string& text, char sep); +void stealLinebreaks(std::string& code, int num); +std::string strip(const std::string& s); +std::string makeSafe(const std::string& s); +inline bool isNameChar(char c) { + return (c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_'; +} + +// file tools +std::string readFile(const std::string&); +bool fileExists(const std::string& name); +void writeFile(const std::string& name, const std::string& text); + +struct Sink { + Sink(const std::string& infile,const std::string& outfile); + void write(); + + std::ostringstream inplace; + std::ostringstream link; + bool isHeader; + std::string infile; +private: + std::string filename; +}; + +// simple string-based stack +struct BracketStack { + bool empty() { return stack.empty(); } + char top() { return empty() ? '\0' : *(stack.rbegin()); } + void push_back(char c) { stack += c; } + char pop() { if (empty()) return '\0'; char c = *(stack.rbegin()); stack.erase(stack.end()-1); return c; } + + std::string stack; +}; + + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/manta.h b/source/blender/python/manta_full/source/pwrapper/manta.h new file mode 100644 index 00000000000..87e5c95252d --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/manta.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main + * + ******************************************************************************/ + +#ifndef _MANTA_H +#define _MANTA_H + +// Remove preprocessor keywords, so there won't infere with autocompletion etc. +#define KERNEL +#define PYTHON + +// Define plugin documentation group +// all plugin functions and classes will automatically be added to this group +//! @defgroup Plugins Plugins +//! @defgroup PyClasses Classes exposed to Python + +#include "general.h" +#include "vectorbase.h" +#include "registry.h" +#include "pclass.h" +#include "pconvert.h" +#include "fluidsolver.h" + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/manta_api.cpp b/source/blender/python/manta_full/source/pwrapper/manta_api.cpp new file mode 100644 index 00000000000..c14e15472ef --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/manta_api.cpp @@ -0,0 +1,27 @@ +#include "Python.h" +//#include "manta_api.h" +#include "manta.h" +#include "../general.h" + +using namespace std; +using namespace Manta; + +#if PY_MAJOR_VERSION >= 3 +typedef wchar_t pyChar; +typedef wstring pyString; +#else +typedef char pyChar; +typedef string pyString; +#endif + +//#ifdef __cplusplus +extern "C" { +//#endif +PyObject * PyInit_Manta(void) +{ + return Pb::PyInit_Main_Obj(); +} + +//#ifdef __cplusplus +} +//#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/manta_api.h b/source/blender/python/manta_full/source/pwrapper/manta_api.h new file mode 100644 index 00000000000..3fbfe6f522c --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/manta_api.h @@ -0,0 +1,6 @@ +#ifndef _MANTA_API_H_ +#define _MANTA_API_H_ +//#include "pythonInclude.h" +PyObject * PyInit_Manta(void); +PyMODINIT_FUNC PyInit_Main_Link(void); +#endif diff --git a/source/blender/python/manta_full/source/pwrapper/pclass.cpp b/source/blender/python/manta_full/source/pwrapper/pclass.cpp new file mode 100644 index 00000000000..13a8240c609 --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pclass.cpp @@ -0,0 +1,178 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Functions for property setting/getting via python + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include "structmember.h" +#include "manta.h" +#include "general.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// Free functions + +#ifdef GUI + extern void updateQtGui(bool full, int frame, const std::string& curPlugin); +#else + inline void updateQtGui(bool full, int frame, const std::string& curPlugin) {} +#endif + +void pbPreparePlugin(FluidSolver* parent, const string& name) { + if (parent) + parent->pluginStart(name); +} + +void pbFinalizePlugin(FluidSolver *parent, const string& name) { + if (parent) { + parent->pluginStop(name); + } + + // GUI update, also print name of parent if there's more than one + std::ostringstream msg; + if (name != "FluidSolver::step") + { + if(parent && (parent->getNumInstances()>0) ) msg << parent->getName() << string("."); + msg << name; + } + updateQtGui(false, 0, msg.str() ); + + // name unnamed PbClass Objects from var name + PbClass::renameObjects(); +} + +void pbSetError(const string& fn, const string& ex) { + cout << "Error in " << fn << endl; + if (!ex.empty()) + PyErr_SetString(PyExc_RuntimeError, ex.c_str()); +} + +//****************************************************************************** +// Helpers + +string PbTypeVec::str() const { + if (T.empty()) return ""; + string s="<"; + for (int i=0; i<(int)T.size(); i++) { + s += T[i].str(); + s += (i!=(int)T.size()-1) ? ',' : '>'; + } + return s; +} +string PbType::str() const { + if (S=="float") return "Real"; + if (S=="manta.vec3") return "Vec3"; + return S; +} + +//****************************************************************************** +// PbClass + +vector<PbClass*> PbClass::mInstances; + +PbClass::PbClass(FluidSolver* parent, const string& name, PyObject* obj) + : mMutex(), mParent(parent), mPyObject(obj), mName(name), mHidden(false) +{ +} + +PbClass::PbClass(const PbClass& a) : mMutex(), mParent(a.mParent), mPyObject(0), mName("_unnamed"), mHidden(false) +{ +} + + +PbClass::~PbClass() { + for(vector<PbClass*>::iterator it = mInstances.begin(); it != mInstances.end(); ++it) { + if (*it == this) { + mInstances.erase(it); + break; + } + } +} + +void PbClass::lock() { + mMutex.lock(); +} +void PbClass::unlock() { + mMutex.unlock(); +} +bool PbClass::tryLock() { + return mMutex.tryLock(); +} + +PbClass* PbClass::getInstance(int idx) { + if (idx<0 || idx > (int)mInstances.size()) + errMsg("PbClass::getInstance(): invalid index"); + return mInstances[idx]; +} + +int PbClass::getNumInstances() { + return mInstances.size(); +} + +bool PbClass::isNullRef(PyObject* obj) { + return PyLong_Check(obj) && PyLong_AsDouble(obj)==0; +} + +void PbClass::registerObject(PyObject* obj, PbArgs* args) { + // cross link + Pb::setReference(this, obj); + mPyObject = obj; + + mInstances.push_back(this); + + if (args) { + string _name = args->getOpt<std::string>("name",-1,""); + if (!_name.empty()) setName(_name); + } +} + +PbClass* PbClass::createPyObject(const string& classname, const string& name, PbArgs& args, PbClass* parent) { + return Pb::createPy(classname,name,args,parent); +} + +void PbClass::checkParent() { + if (getParent() == NULL) { + errMsg("New class " + mName + ": no parent given -- specify using parent=xxx !"); + } +} +//! Assign unnamed PbClass objects their Python variable name +void PbClass::renameObjects() { + PyObject* sys_mod_dict = PyImport_GetModuleDict(); + PyObject* loc_mod = PyMapping_GetItemString(sys_mod_dict, (char*)"__main__"); + if (!loc_mod) return; + PyObject* locdict = PyObject_GetAttrString(loc_mod, "__dict__"); + if (!locdict) return; + + // iterate all PbClass instances + for (size_t i=0; i<mInstances.size(); i++) { + PbClass* obj = mInstances[i]; + if (obj->getName().empty()) { + // empty, try to find instance in module local dictionary + + PyObject *lkey, *lvalue; + Py_ssize_t lpos = 0; + while (PyDict_Next(locdict, &lpos, &lkey, &lvalue)) { + if (lvalue == obj->mPyObject) { + string varName = fromPy<string>(PyObject_Str(lkey)); + obj->setName(varName); + //cout << "assigning variable name '" << varName << "' to unnamed instance" << endl; + break; + } + } + } + } + Py_DECREF(locdict); + Py_DECREF(loc_mod); +} + +} // namespace diff --git a/source/blender/python/manta_full/source/pwrapper/pclass.h b/source/blender/python/manta_full/source/pwrapper/pclass.h new file mode 100644 index 00000000000..418d0d8dfaf --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pclass.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for all Python-exposed classes + * + ******************************************************************************/ + +// ----------------------------------------------------------------- +// NOTE: +// Do not include this file in user code, include "manta.h" instead +// ----------------------------------------------------------------- + +#ifdef _MANTA_H +#ifndef _PTYPE_H +#define _PTYPE_H + +#include <string> +#include <vector> +#include <map> + +#ifdef GUI +# include <QMutex> +#else +struct QMutex { + void lock() {}; + void unlock() {}; + bool tryLock() {return true;}; +}; +#endif + +namespace Manta { +struct PbClassData; +class FluidSolver; +class PbArgs; + +struct PbType { + std::string S; + std::string str() const; +}; +struct PbTypeVec { + std::vector<PbType> T; + std::string str() const; +}; + +//! Base class for all classes exposed to Python +class PbClass { +public: + PbClass(FluidSolver* parent, const std::string& name="", PyObject* obj=NULL); + PbClass(const PbClass& a); + virtual ~PbClass(); + + // basic property setter/getters + void setName(const std::string& name) { mName = name; } + std::string getName() const { return mName; } + PyObject* getPyObject() const { return mPyObject; } + void registerObject(PyObject* obj, PbArgs* args); + FluidSolver* getParent() const { return mParent; } + void setParent(FluidSolver* v) { mParent = v; } + void checkParent(); + + // hidden flag for GUI, debug output + inline bool isHidden() { return mHidden; } + inline void setHidden(bool v) { mHidden = v; } + + void lock(); + void unlock(); + bool tryLock(); + + // PbClass instance registry + static int getNumInstances(); + static PbClass* getInstance(int index); + static void renameObjects(); + + // converters + static bool isNullRef(PyObject* o); + static PbClass* createPyObject(const std::string& classname, const std::string& name, PbArgs& args, PbClass *parent); + inline bool canConvertTo(const std::string& classname) { return Pb::canConvert(mPyObject, classname); } + +protected: + QMutex mMutex; + FluidSolver* mParent; + PyObject* mPyObject; + std::string mName; + bool mHidden; + + static std::vector<PbClass*> mInstances; +}; + +//!\cond Register + +void pbFinalizePlugin(FluidSolver* parent, const std::string& name); +void pbPreparePlugin(FluidSolver* parent, const std::string& name); +void pbSetError(const std::string& fn, const std::string& ex); + +//!\endcond + +} // namespace + +#endif +#endif diff --git a/source/blender/python/manta_full/source/pwrapper/pconvert.cpp b/source/blender/python/manta_full/source/pwrapper/pconvert.cpp new file mode 100644 index 00000000000..75ad14e60dc --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pconvert.cpp @@ -0,0 +1,422 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Python argument wrappers and conversion tools + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include <sstream> +#include <algorithm> +#include "vectorbase.h" +#include "manta.h" + +using namespace std; + +//****************************************************************************** +// Explicit definition and instantiation of python object converters + +namespace Manta { + +extern PyTypeObject PbVec3Type; +struct PbVec3 { + PyObject_HEAD + float data[3]; +}; + +PyObject* getPyNone() { + Py_INCREF(Py_None); + return Py_None; +} +PyObject* incref(PyObject* obj) { + Py_INCREF(obj); + return obj; +} + + +/*template<> PyObject* toPy<PyObject*>(PyObject* obj) { + return obj; +}*/ +template<> PyObject* toPy<int>( const int& v) { + return PyLong_FromLong(v); +} +/*template<> PyObject* toPy<char*>(const (char*) & val) { + return PyUnicode_DecodeLatin1(val,strlen(val),"replace"); +}*/ +template<> PyObject* toPy<string>( const string& val) { + return PyUnicode_DecodeLatin1(val.c_str(),val.length(),"replace"); +} +template<> PyObject* toPy<float>( const float& v) { + return PyFloat_FromDouble(v); +} +template<> PyObject* toPy<double>( const double& v) { + return PyFloat_FromDouble(v); +} +template<> PyObject* toPy<bool>( const bool& v) { + return PyBool_FromLong(v); +} +template<> PyObject* toPy<Vec3i>(const Vec3i& v) { + float x=(float)v.x, y=(float)v.y, z=(float)v.z; + return PyObject_CallFunction((PyObject*)&PbVec3Type, (char*)"fff", x, y, z); +} +template<> PyObject* toPy<Vec3>(const Vec3& v) { + float x=(float)v.x, y=(float)v.y, z=(float)v.z; + return PyObject_CallFunction((PyObject*)&PbVec3Type, (char*)"fff", x, y, z); +} +template<> PyObject* toPy<PbClass*>(const PbClass_Ptr& obj) { + return obj->getPyObject(); +} + +template<> float fromPy<float>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + errMsg("argument is not a float"); +} +template<> double fromPy<double>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + errMsg("argument is not a double"); +} +template<> PyObject* fromPy<PyObject*>(PyObject *obj) { + return obj; +} +template<> int fromPy<int>(PyObject *obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + if (PyFloat_Check(obj)) { + double a = PyFloat_AsDouble(obj); + if (fabs(a-floor(a+0.5)) > 1e-5) + errMsg("argument is not an int"); + return (int) (a+0.5); + } + errMsg("argument is not an int"); +} +template<> string fromPy<string>(PyObject *obj) { + if (PyUnicode_Check(obj)) + return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); +#if PY_MAJOR_VERSION <= 2 + else if (PyString_Check(obj)) + return PyString_AsString(obj); +#endif + else + errMsg("argument is not a string"); +} +template<> const char* fromPy<const char*>(PyObject *obj) { + if (PyUnicode_Check(obj)) + return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); +#if PY_MAJOR_VERSION <= 2 + else if (PyString_Check(obj)) + return PyString_AsString(obj); +#endif + else errMsg("argument is not a string"); +} +template<> bool fromPy<bool>(PyObject *obj) { + if (!PyBool_Check(obj)) errMsg("argument is not a boolean"); + return PyLong_AsLong(obj) != 0; +} +template<> Vec3 fromPy<Vec3>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) { + return Vec3(((PbVec3*)obj)->data); + } + else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return Vec3(fromPy<Real>(PyTuple_GetItem(obj,0)), + fromPy<Real>(PyTuple_GetItem(obj,1)), + fromPy<Real>(PyTuple_GetItem(obj,2))); + } + errMsg("argument is not a Vec3"); +} +template<> Vec3i fromPy<Vec3i>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) { + return toVec3iChecked(((PbVec3*)obj)->data); + } + else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return Vec3i(fromPy<int>(PyTuple_GetItem(obj,0)), + fromPy<int>(PyTuple_GetItem(obj,1)), + fromPy<int>(PyTuple_GetItem(obj,2))); + } + errMsg("argument is not a Vec3i"); +} +template<> PbType fromPy<PbType>(PyObject* obj) { + PbType pb = {""}; + if (!PyType_Check(obj)) + return pb; + + const char* tname = ((PyTypeObject*)obj)->tp_name; + pb.S = tname; + return pb; +} +template<> PbTypeVec fromPy<PbTypeVec>(PyObject* obj) { + PbTypeVec vec; + if (PyType_Check(obj)) { + vec.T.push_back(fromPy<PbType>(obj)); + } else if (PyTuple_Check(obj)) { + int sz = PyTuple_Size(obj); + for (int i=0; i< sz; i++) + vec.T.push_back(fromPy<PbType>(PyTuple_GetItem(obj,i))); + } + else + errMsg("argument is not a type tuple"); + return vec; +} + +template<class T> T* tmpAlloc(PyObject* obj,std::vector<void*>* tmp) { + if (!tmp) throw Error("dynamic de-ref not supported for this type"); + void* ptr = malloc(sizeof(T)); + tmp->push_back(ptr); + + *((T*)ptr) = fromPy<T>(obj); + return (T*)ptr; +} +template<> float* fromPyPtr<float>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<float>(obj,tmp); } +template<> double* fromPyPtr<double>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<double>(obj,tmp); } +template<> int* fromPyPtr<int>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<int>(obj,tmp); } +template<> std::string* fromPyPtr<std::string>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<std::string>(obj,tmp); } +template<> bool* fromPyPtr<bool>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<bool>(obj,tmp); } +template<> Vec3* fromPyPtr<Vec3>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<Vec3>(obj,tmp); } +template<> Vec3i* fromPyPtr<Vec3i>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<Vec3i>(obj,tmp); } + +template<> bool isPy<float>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + return PyFloat_Check(obj) || PyLong_Check(obj); +} +template<> bool isPy<double>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + return PyFloat_Check(obj) || PyLong_Check(obj); +} +template<> bool isPy<PyObject*>(PyObject *obj) { + return true; +} +template<> bool isPy<int>(PyObject *obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + if (PyLong_Check(obj)) return true; + if (PyFloat_Check(obj)) { + double a = PyFloat_AsDouble(obj); + return fabs(a-floor(a+0.5)) < 1e-5; + } + return false; +} +template<> bool isPy<string>(PyObject *obj) { + if (PyUnicode_Check(obj)) return true; +#if PY_MAJOR_VERSION <= 2 + if (PyString_Check(obj)) return true; +#endif + return false; +} +template<> bool isPy<const char*>(PyObject *obj) { + if (PyUnicode_Check(obj)) return true; +#if PY_MAJOR_VERSION <= 2 + if (PyString_Check(obj)) return true; +#endif + return false; +} +template<> bool isPy<bool>(PyObject *obj) { + return PyBool_Check(obj); +} +template<> bool isPy<Vec3>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) return true; + if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return isPy<Real>(PyTuple_GetItem(obj,0)) && + isPy<Real>(PyTuple_GetItem(obj,1)) && + isPy<Real>(PyTuple_GetItem(obj,2)); + } + return false; +} +template<> bool isPy<Vec3i>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) return true; + if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return isPy<int>(PyTuple_GetItem(obj,0)) && + isPy<int>(PyTuple_GetItem(obj,1)) && + isPy<int>(PyTuple_GetItem(obj,2)); + } + return false; +} +template<> bool isPy<PbType>(PyObject* obj) { + return PyType_Check(obj); +} + +//****************************************************************************** +// PbArgs class defs + +PbArgs PbArgs::EMPTY(NULL,NULL); + +PbArgs::PbArgs(PyObject* linarg, PyObject* dict) : mLinArgs(0), mKwds(0) { + setup(linarg, dict); +} +PbArgs::~PbArgs() { + for(int i=0; i<(int)mTmpStorage.size(); i++) + free(mTmpStorage[i]); + mTmpStorage.clear(); +} + +void PbArgs::copy(PbArgs& a) { + mKwds = a.mKwds; + mData = a.mData; + mLinData = a.mLinData; + mLinArgs = a.mLinArgs; + +} +void PbArgs::clear() { + mLinArgs = 0; + mKwds = 0; + mData.clear(); + mLinData.clear(); +} + +PbArgs& PbArgs::operator=(const PbArgs& a) { +// mLinArgs = 0; +// mKwds = 0; + return *this; +} + +void PbArgs::setup(PyObject* linarg, PyObject* dict) { + if (dict) { + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + DataElement el; + el.obj = value; + el.visited = false; + mData[fromPy<string>(key)] = el; + } + mKwds = dict; + } + if (linarg) { + size_t len = PyTuple_Size(linarg); + for (size_t i=0; i<len; i++) { + DataElement el; + el.obj = PyTuple_GetItem(linarg, i); + el.visited = false; + mLinData.push_back(el); + } + mLinArgs = linarg; + } +} + +void PbArgs::addLinArg(PyObject* obj) { + DataElement el = { obj, false }; + mLinData.push_back(el); +} + +void PbArgs::check() { + if (has("nocheck")) return; + + for(map<string, DataElement>::iterator it = mData.begin(); it != mData.end(); it++) { + if (!it->second.visited) + errMsg("Argument '" + it->first + "' unknown"); + } + for(size_t i=0; i<mLinData.size(); i++) { + if (!mLinData[i].visited) { + stringstream s; + s << "Function does not read argument number #" << i; + errMsg(s.str()); + } + } +} + +FluidSolver* PbArgs::obtainParent() { + FluidSolver* solver = getPtrOpt<FluidSolver>("solver",-1,NULL); + if (solver != 0) return solver; + + // previous versions returned NULL for different parent solvers of args in the following two loops + // NULL pointer for solvers is not allowed anymore (unless explicitly specified with "noparent" keyword) + // thus, these checks are disabled for now - future version should also explicitly + // allow/disallow multiple solver parents... + + for(map<string, DataElement>::iterator it = mData.begin(); it != mData.end(); it++) { + PbClass* obj = Pb::objFromPy(it->second.obj); + + if (obj) { + if (solver == NULL) + solver = obj->getParent(); + //else if (solver != obj->getParent()) + //return NULL; + } + } + for(vector<DataElement>::iterator it = mLinData.begin(); it != mLinData.end(); it++) { + PbClass* obj = Pb::objFromPy(it->obj); + + if (obj) { + if (solver == NULL) + solver = obj->getParent(); + //else if (solver != obj->getParent()) + //return NULL; + } + } + + // allow plugins without solver + if (!solver) + errMsg("Solver cannot be deduced from arguments, specify using argument 'solver=xxx'"); + return solver; +} + +void PbArgs::visit(int number, const string& key) { + if (number >= 0 && number < (int)mLinData.size()) + mLinData[number].visited = true; + map<string, DataElement>::iterator lu = mData.find(key); + if (lu != mData.end()) + lu->second.visited = true; +} + +PyObject* PbArgs::getItem(const std::string& key, bool strict, ArgLocker* lk) { + map<string, DataElement>::iterator lu = mData.find(key); + if (lu == mData.end()) { + if (strict) + errMsg ("Argument '" + key + "' is not defined."); + return NULL; + } + PbClass* pbo = Pb::objFromPy(lu->second.obj); + // try to lock + if (pbo && lk) lk->add(pbo); + return lu->second.obj; +} + +PyObject* PbArgs::getItem(size_t number, bool strict, ArgLocker* lk) { + if (number >= mLinData.size()) { + if (!strict) + return NULL; + stringstream s; + s << "Argument number #" << number << " not specified."; + errMsg(s.str()); + } + PbClass* pbo = Pb::objFromPy(mLinData[number].obj); + // try to lock + if (pbo && lk) lk->add(pbo); + return mLinData[number].obj; +} + +//****************************************************************************** +// ArgLocker class defs + +void ArgLocker::add(PbClass* p) { + if (find(locks.begin(), locks.end(), p) == locks.end()) { + locks.push_back(p); + p->lock(); + } +} +ArgLocker::~ArgLocker() { + for (size_t i=0; i<locks.size(); i++) + locks[i]->unlock(); + locks.clear(); +} + +} // namespace diff --git a/source/blender/python/manta_full/source/pwrapper/pconvert.h b/source/blender/python/manta_full/source/pwrapper/pconvert.h new file mode 100644 index 00000000000..7b8721254a5 --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pconvert.h @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Python argument wrappers and conversion tools + * + ******************************************************************************/ + +// ----------------------------------------------------------------- +// NOTE: +// Do not include this file in user code, include "manta.h" instead +// ----------------------------------------------------------------- + +#ifdef _MANTA_H +#ifndef _PCONVERT_H +#define _PCONVERT_H + +#include <string> +#include <map> +#include <vector> + +namespace Manta { +template<class T> class Grid; + + +//! Locks the given PbClass Arguments until ArgLocker goes out of scope +struct ArgLocker { + void add(PbClass* p); + ~ArgLocker(); + std::vector<PbClass*> locks; +}; + +PyObject* getPyNone(); + +// for PbClass-derived classes +template<class T> T* fromPyPtr(PyObject* obj, std::vector<void*>* tmp) { + if (PbClass::isNullRef(obj)) + return 0; + PbClass* pbo = Pb::objFromPy(obj); + const std::string& type = Namify<T>::S; + if (!pbo || !(pbo->canConvertTo(type))) + throw Error("can't convert argument to " + type+"*"); + return (T*)(pbo); +} + +template<> float* fromPyPtr<float>(PyObject* obj, std::vector<void*>* tmp); +template<> double* fromPyPtr<double>(PyObject* obj, std::vector<void*>* tmp); +template<> int* fromPyPtr<int>(PyObject* obj, std::vector<void*>* tmp); +template<> std::string* fromPyPtr<std::string>(PyObject* obj, std::vector<void*>* tmp); +template<> bool* fromPyPtr<bool>(PyObject* obj, std::vector<void*>* tmp); +template<> Vec3* fromPyPtr<Vec3>(PyObject* obj, std::vector<void*>* tmp); +template<> Vec3i* fromPyPtr<Vec3i>(PyObject* obj, std::vector<void*>* tmp); + +PyObject* incref(PyObject* obj); +template<class T> PyObject* toPy(const T& v) { + PyObject* obj = v.getPyObject(); + if (obj) { + return incref(obj); + } + T* co = new T (v); + const std::string& type = Namify<typename remove_pointers<T>::type>::S; + return Pb::copyObject(co,type); +} +template<class T> bool isPy(PyObject* obj) { + if (PbClass::isNullRef(obj)) + return false; + PbClass* pbo = Pb::objFromPy(obj); + const std::string& type = Namify<typename remove_pointers<T>::type>::S; + return pbo && pbo->canConvertTo(type); +} + +template<class T> T fromPy(PyObject* obj) { + throw Error("Unknown type conversion. Did you pass a PbClass by value? (you shouldn't)"); +} + +// builtin types +template<> float fromPy<float>(PyObject* obj); +template<> double fromPy<double>(PyObject* obj); +template<> int fromPy<int>(PyObject *obj); +template<> PyObject* fromPy<PyObject*>(PyObject *obj); +template<> std::string fromPy<std::string>(PyObject *obj); +template<> const char* fromPy<const char*>(PyObject *obj); +template<> bool fromPy<bool>(PyObject *obj); +template<> Vec3 fromPy<Vec3>(PyObject* obj); +template<> Vec3i fromPy<Vec3i>(PyObject* obj); +template<> PbType fromPy<PbType>(PyObject* obj); +template<> PbTypeVec fromPy<PbTypeVec>(PyObject* obj); + +template<> PyObject* toPy<int>( const int& v); +template<> PyObject* toPy<std::string>( const std::string& val); +template<> PyObject* toPy<float>( const float& v); +template<> PyObject* toPy<double>( const double& v); +template<> PyObject* toPy<bool>( const bool& v); +template<> PyObject* toPy<Vec3i>( const Vec3i& v); +template<> PyObject* toPy<Vec3>( const Vec3& v); +typedef PbClass* PbClass_Ptr; +template<> PyObject* toPy<PbClass*>( const PbClass_Ptr & obj); + +template<> bool isPy<float>(PyObject* obj); +template<> bool isPy<double>(PyObject* obj); +template<> bool isPy<int>(PyObject *obj); +template<> bool isPy<PyObject*>(PyObject *obj); +template<> bool isPy<std::string>(PyObject *obj); +template<> bool isPy<const char*>(PyObject *obj); +template<> bool isPy<bool>(PyObject *obj); +template<> bool isPy<Vec3>(PyObject* obj); +template<> bool isPy<Vec3i>(PyObject* obj); +template<> bool isPy<PbType>(PyObject* obj); + +//! Encapsulation of python arguments +class PbArgs { +public: + PbArgs(PyObject *linargs = NULL, PyObject* dict = NULL); + ~PbArgs(); + void setup(PyObject *linargs = NULL, PyObject* dict = NULL); + + void check(); + FluidSolver* obtainParent(); + + inline int numLinArgs() { return mLinData.size(); } + + inline bool has(const std::string& key) { + return getItem(key, false) != NULL; + } + + inline PyObject* linArgs() { return mLinArgs; } + inline PyObject* kwds() { return mKwds; } + + void addLinArg(PyObject* obj); + + template<class T> inline void add(const std::string& key, T arg) { + DataElement el = { toPy(arg), false }; + mData[key] = el; + } + template<class T> inline T get(const std::string& key, int number=-1, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPy<T>(o); + o = getItem(number, false, lk); + if (o) return fromPy<T>(o); + errMsg ("Argument '" + key + "' is not defined."); + } + template<class T> inline T getOpt(const std::string& key, int number, T defarg, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPy<T>(o); + if (number >= 0) o = getItem(number, false, lk); + return (o) ? fromPy<T>(o) : defarg; + } + template<class T> inline T* getPtrOpt(const std::string& key, int number, T* defarg, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPyPtr<T>(o,&mTmpStorage); + if (number >= 0) o = getItem(number, false, lk); + return o ? fromPyPtr<T>(o,&mTmpStorage) : defarg; + } + template<class T> inline T* getPtr(const std::string& key, int number = -1, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPyPtr<T>(o,&mTmpStorage); + o = getItem(number, false, lk); + if(o) return fromPyPtr<T>(o,&mTmpStorage); + errMsg ("Argument '" + key + "' is not defined."); + } + + + // automatic template type deduction + template<class T> bool typeCheck(int num, const std::string& name) { + PyObject* o = getItem(name, false, 0); + if (!o) + o = getItem(num, false, 0); + return o ? isPy<typename remove_pointers<T>::type>(o) : false; + } + + PbArgs& operator=(const PbArgs& a); // dummy + void copy(PbArgs& a); + void clear(); + void visit(int num, const std::string& key); + + static PbArgs EMPTY; + +protected: + PyObject* getItem(const std::string& key, bool strict, ArgLocker* lk = NULL); + PyObject* getItem(size_t number, bool strict, ArgLocker* lk = NULL); + + struct DataElement { + PyObject *obj; + bool visited; + }; + std::map<std::string, DataElement> mData; + std::vector<DataElement> mLinData; + PyObject* mLinArgs, *mKwds; + std::vector<void*> mTmpStorage; +}; + + +} // namespace +#endif +#endif diff --git a/source/blender/python/manta_full/source/pwrapper/pvec3.cpp b/source/blender/python/manta_full/source/pwrapper/pvec3.cpp new file mode 100644 index 00000000000..0743fe6914b --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pvec3.cpp @@ -0,0 +1,286 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vec3 class extension for python + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include <math.h> +#include <string> +#include <sstream> +#include <limits> +#include "vectorbase.h" +#include "structmember.h" +#include "manta.h" + +using namespace std; + +namespace Manta { + +extern PyTypeObject PbVec3Type; + +struct PbVec3 { + PyObject_HEAD + float data[3]; +}; + +static void PbVec3Dealloc(PbVec3* self) { + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * PbVec3New(PyTypeObject *type, PyObject *args, PyObject *kwds) { + return type->tp_alloc(type, 0); +} + +static int PbVec3Init(PbVec3 *self, PyObject *args, PyObject *kwds) { + + float x1 = numeric_limits<float>::quiet_NaN(), x2=x1, x3=x1; + if (!PyArg_ParseTuple(args,"|fff",&x1, &x2, &x3)) + return -1; + + if (!c_isnan(x1)) { + self->data[0] = x1; + if (!c_isnan(x2) && !c_isnan(x3)) { + self->data[1] = x2; + self->data[2] = x3; + } else { + self->data[1] = x1; + self->data[2] = x1; + } + } else { + self->data[0] = 0; + self->data[1] = 0; + self->data[2] = 0; + } + return 0; +} + +static PyObject* PbVec3Repr(PbVec3* self) { + Manta::Vec3 v(self->data[0], self->data[1], self->data[2]); + return PyUnicode_FromFormat(v.toString().c_str()); +} + +static PyMemberDef PbVec3Members[] = { + {(char*)"x", T_FLOAT, offsetof(PbVec3, data), 0, (char*)"X"}, + {(char*)"y", T_FLOAT, offsetof(PbVec3, data)+sizeof(float), 0, (char*)"Y"}, + {(char*)"z", T_FLOAT, offsetof(PbVec3, data)+sizeof(float)*2, 0, (char*)"Z"}, + {NULL} // Sentinel +}; + +static PyMethodDef PbVec3Methods[] = { + //{"name", (PyCFunction)Noddy_name, METH_NOARGS, "Return the name, combining the first and last name" }, + {NULL} // Sentinel +}; + +// operator overloads + +inline PyObject* PbNew(const Vec3& a) { + PbVec3* obj = (PbVec3*)PbVec3New(&PbVec3Type,0,0); + obj->data[0] = a.x; + obj->data[1] = a.y; + obj->data[2] = a.z; + return (PyObject*)obj; +} + +#define CONVERTVEC(obj) \ + Vec3 v##obj; \ + if (PyObject_TypeCheck(obj, &PbVec3Type)) \ + v##obj = Vec3(&( ((PbVec3*)obj)->data[0])); \ + else if (PyFloat_Check(obj)) \ + v##obj = Vec3(PyFloat_AsDouble(obj)); \ + else if (PyLong_Check(obj)) \ + v##obj = Vec3(PyLong_AsDouble(obj)); \ + else { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + +#define OPHEADER \ + if (!PyObject_TypeCheck(a, &PbVec3Type) && !PyObject_TypeCheck(b, &PbVec3Type)) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + CONVERTVEC(a) \ + CONVERTVEC(b) + +#define OPHEADER1 \ + if (!PyObject_TypeCheck(a, &PbVec3Type)) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + CONVERTVEC(a) + +PyObject* PbVec3Add(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va+vb); +} + +PyObject* PbVec3Sub(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va-vb); +} + +PyObject* PbVec3Mult(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va*vb); +} + +PyObject* PbVec3Div(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va/vb); +} + +PyObject* PbVec3Negative(PyObject* a) { + OPHEADER1 + return PbNew(-va); +} + +// numbers are defined subtely different in Py3 (WTF?) +#if PY_MAJOR_VERSION >= 3 +static PyNumberMethods PbVec3NumberMethods = { + (binaryfunc)PbVec3Add, // binaryfunc nb_add; + (binaryfunc)PbVec3Sub, // binaryfunc nb_sub; + (binaryfunc)PbVec3Mult, // binaryfunc nb_mult; + 0, // binaryfunc nb_remainder; + 0, // binaryfunc nb_divmod; + 0, // ternaryfunc nb_power; + (unaryfunc)PbVec3Negative, // unaryfunc nb_negative; + 0, // unaryfunc nb_positive; + 0, // unaryfunc nb_absolute; + 0, // inquiry nb_bool; + 0, // unaryfunc nb_invert; + 0, // binaryfunc nb_lshift; + 0, // binaryfunc nb_rshift; + 0, // binaryfunc nb_and; + 0, // binaryfunc nb_xor; + 0, // binaryfunc nb_or; + 0, // unaryfunc nb_int; + 0, // void *nb_reserved; + 0, // unaryfunc nb_float; + 0, // binaryfunc nb_inplace_add; + 0, // binaryfunc nb_inplace_subtract; + 0, // binaryfunc nb_inplace_multiply; + 0, // binaryfunc nb_inplace_remainder; + 0, // ternaryfunc nb_inplace_power; + 0, // binaryfunc nb_inplace_lshift; + 0, // binaryfunc nb_inplace_rshift; + 0, // binaryfunc nb_inplace_and; + 0, // binaryfunc nb_inplace_xor; + 0, // binaryfunc nb_inplace_or; + + 0, // binaryfunc nb_floor_divide; + (binaryfunc)PbVec3Div, // binaryfunc nb_true_divide; + 0, // binaryfunc nb_inplace_floor_divide; + 0, // binaryfunc nb_inplace_true_divide; + + 0 // unaryfunc nb_index; +}; +#else +static PyNumberMethods PbVec3NumberMethods = { + (binaryfunc)PbVec3Add, // binaryfunc nb_add; + (binaryfunc)PbVec3Sub, // binaryfunc nb_sub; + (binaryfunc)PbVec3Mult, // binaryfunc nb_mult; + 0, // binaryfunc nb_divide; + 0, // binaryfunc nb_remainder; + 0, // binaryfunc nb_divmod; + 0, // ternaryfunc nb_power; + (unaryfunc)PbVec3Negative, // unaryfunc nb_negative; + 0, // unaryfunc nb_positive; + 0, // unaryfunc nb_absolute; + 0, // inquiry nb_nonzero; + 0, // unaryfunc nb_invert; + 0, // binaryfunc nb_lshift; + 0, // binaryfunc nb_rshift; + 0, // binaryfunc nb_and; + 0, // binaryfunc nb_xor; + 0, // binaryfunc nb_or; + 0, // coercion nb_coerce; + 0, // unaryfunc nb_int; + 0, // unaryfunc nb_long; + 0, // unaryfunc nb_float; + 0, // unaryfunc nb_oct; + 0, // unaryfunc nb_hex; + 0, // binaryfunc nb_inplace_add; + 0, // binaryfunc nb_inplace_subtract; + 0, // binaryfunc nb_inplace_multiply; + 0, // binaryfunc nb_inplace_divide; + 0, // binaryfunc nb_inplace_remainder; + 0, // ternaryfunc nb_inplace_power; + 0, // binaryfunc nb_inplace_lshift; + 0, // binaryfunc nb_inplace_rshift; + 0, // binaryfunc nb_inplace_and; + 0, // binaryfunc nb_inplace_xor; + 0, // binaryfunc nb_inplace_or; + 0, // binaryfunc nb_floor_divide; + (binaryfunc)PbVec3Div, // binaryfunc nb_true_divide; + 0, // binaryfunc nb_inplace_floor_divide; + 0, // binaryfunc nb_inplace_true_divide; + 0, // unaryfunc nb_index; +}; +#endif + +PyTypeObject PbVec3Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "manta.vec3", /* tp_name */ + sizeof(PbVec3), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PbVec3Dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)PbVec3Repr, /* tp_repr */ + &PbVec3NumberMethods, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + "float vector type", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PbVec3Methods, /* tp_methods */ + PbVec3Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PbVec3Init, /* tp_init */ + 0, /* tp_alloc */ + PbVec3New, /* tp_new */ +}; + +inline PyObject* castPy(PyTypeObject* p) { + return reinterpret_cast<PyObject*>(static_cast<void*>(p)); +} + +void PbVecInitialize(PyObject* module) { + if (PyType_Ready(&PbVec3Type) < 0) errMsg("can't initialize Vec3 type"); + + Py_INCREF(castPy(&PbVec3Type)); + PyModule_AddObject(module, "vec3", (PyObject *)&PbVec3Type); +} +const static Pb::Register _REG(PbVecInitialize); + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/pymain.cpp b/source/blender/python/manta_full/source/pwrapper/pymain.cpp new file mode 100644 index 00000000000..5dc763daa63 --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pymain.cpp @@ -0,0 +1,112 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * Main file
+ *
+ ******************************************************************************/
+
+#include "pythonInclude.h"
+#include <stdio.h>
+#include "manta.h"
+#include "general.h"
+#include "wchar.h"
+#include "pymain.h"
+
+namespace Manta {
+ extern void guiMain(int argc, char* argv[]);
+ extern void guiWaitFinish();
+}
+
+using namespace std;
+using namespace Manta;
+
+#if PY_MAJOR_VERSION >= 3
+typedef wchar_t pyChar;
+typedef wstring pyString;
+#else
+typedef char pyChar;
+typedef string pyString;
+#endif
+
+//*****************************************************************************
+// main...
+
+void runScript(vector<string>& args) {
+ string filename = args[0];
+
+ // Initialize extension classes and wrappers
+ srand(0);
+ Pb::setup(filename, args);
+
+ // Pass through the command line arguments
+ // for Py3k compatability, convert to wstring
+ vector<pyString> pyArgs(args.size());
+ const pyChar ** cargs = new const pyChar* [args.size()];
+ for (size_t i=0; i<args.size(); i++) {
+ pyArgs[i] = pyString(args[i].begin(), args[i].end());
+ cargs[i] = pyArgs[i].c_str();
+ }
+ PySys_SetArgv( args.size(), (pyChar**) cargs);
+
+ // Try to load python script
+ FILE* fp = fopen(filename.c_str(),"rb");
+ if (fp == NULL) {
+ debMsg("Cannot open '" << filename << "'", 0);
+ Pb::finalize();
+ return;
+ }
+
+ // Run the python script file
+ debMsg("Loading script '" << filename << "'", 0);
+#if defined(WIN32) || defined(_WIN32)
+ // known bug workaround: use simplestring
+ fseek(fp,0,SEEK_END);
+ long filelen=ftell(fp);
+ fseek(fp,0,SEEK_SET);
+ char* buf = new char[filelen+1];
+ fread(buf,filelen,1,fp);
+ buf[filelen] = '\0';
+ fclose(fp);
+ PyRun_SimpleString(buf);
+ delete[] buf;
+#else
+ // for linux, use this as it produces nicer error messages
+ PyRun_SimpleFileEx(fp, filename.c_str(), 1);
+ fclose(fp);
+#endif
+
+ debMsg("Script finished.", 0);
+#ifdef GUI
+ guiWaitFinish();
+#endif
+
+ // finalize
+ Pb::finalize();
+
+ delete [] cargs;
+}
+
+int manta_main(int argc,char* argv[]) {
+ debMsg("Version: "<< buildInfoString() , 1);
+
+#ifdef GUI
+ guiMain(argc, argv);
+#else
+ if (argc<=1) {
+ cerr << "Usage : Syntax is 'manta <config.py>'" << endl;
+ return 1;
+ }
+
+ vector<string> args;
+ for (int i=1; i<argc; i++) args.push_back(argv[i]);
+ runScript(args);
+#endif
+
+ return 0;
+}
diff --git a/source/blender/python/manta_full/source/pwrapper/pymain.h b/source/blender/python/manta_full/source/pwrapper/pymain.h new file mode 100644 index 00000000000..50ee6f796be --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pymain.h @@ -0,0 +1,8 @@ +#ifndef _MY_PYMAIN_H_ +#define _MY_PYMAIN_H_ +#include <vector> +#include <string> +using namespace std; +using namespace Manta; +void runScript(vector<string>& args); +#endif diff --git a/source/blender/python/manta_full/source/pwrapper/pythonInclude.h b/source/blender/python/manta_full/source/pwrapper/pythonInclude.h new file mode 100755 index 00000000000..127fb92449f --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/pythonInclude.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for particle systems + * + ******************************************************************************/ + +#ifndef _PYTHONINCLUDE_H +#define _PYTHONINCLUDE_H + +#if defined(WIN32) || defined(_WIN32) + + // note - we have to include these first! + #include <string> + #include <vector> + #include <iostream> + + #ifdef _DEBUG + + // special handling for windows + // disable linking with debug version of python libs + #undef _DEBUG + #define NDEBUG + #include <Python.h> + #define _DEBUG + #undef NDEBUG + + #else + #include <Python.h> + #endif +#else + #include <Python.h> +#endif + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/registry.cpp b/source/blender/python/manta_full/source/pwrapper/registry.cpp new file mode 100644 index 00000000000..6eebc8c5e32 --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/registry.cpp @@ -0,0 +1,682 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Auto python registry + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include "structmember.h" +#include "manta.h" + +using namespace std; + +const string gDefaultModuleName = "manta"; + +namespace Pb { +//PR FULL MANTA +//****************************************************************************** +// Custom object definition + +struct Method { + Method(const string& n, const string& d, GenericFunction f) : name(n), doc(d), func(f) {} + string name, doc; + GenericFunction func; + + PyMethodDef def() { + PyMethodDef def = {&name[0], (PyCFunction)func, METH_VARARGS | METH_KEYWORDS, &doc[0]}; + return def; + } +}; +struct GetSet { + GetSet() : getter(0),setter(0) {} + GetSet(const string& n, const string& d, Getter g, Setter s) : name(n), doc(d), getter(g), setter(s) {} + string name, doc; + Getter getter; + Setter setter; + + PyGetSetDef def() { + PyGetSetDef def = {&name[0], getter, setter, &doc[0], NULL}; + return def; + } +}; + +struct ClassData { + string cName, pyName; + string cPureName, cTemplate; + InitFunc init; + PyTypeObject typeInfo; + PyNumberMethods numInfo; + //PySequenceMethods seqInfo; + vector<Method> methods; + map<string,GetSet> getset; + map<string,OperatorFunction> ops; + ClassData* baseclass; + string baseclassName; + Constructor constructor; + + vector<PyMethodDef> genMethods; + vector<PyGetSetDef> genGetSet; +}; + +struct PbObject { + PyObject_HEAD + Manta::PbClass *instance; + ClassData *classdef; +}; + +//****************************************************** +// Internal wrapper class + +//! Registers all classes and methods exposed to Python. +/*! This class is only used internally by Pb:: framwork. + * Please use the functionality of PbClass to lookup and translate pointers. */ +class WrapperRegistry { +public: + static WrapperRegistry& instance(); + void addClass(const std::string& name, const std::string& internalName, const std::string& baseclass); + void addExternalInitializer(InitFunc func); + void addMethod(const std::string& classname, const std::string& methodname, GenericFunction method); + void addOperator(const std::string& classname, const std::string& methodname, OperatorFunction method); + void addConstructor(const std::string& classname, Constructor method); + void addGetSet(const std::string& classname, const std::string& property, Getter getfunc, Setter setfunc); + void addPythonPath(const std::string& path); + void addPythonCode(const std::string& file, const std::string& code); + PyObject* createPyObject(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass *parent); + void construct(const std::string& scriptname, const vector<string>& args); + void construct_lite(); + void cleanup(); + void renameObjects(); + void runPreInit(); + PyObject* initModule(); + ClassData* lookup(const std::string& name); + bool canConvert(ClassData* from, ClassData* to); + void addScriptData(const std::string& scriptname, const vector<string>& args); +private: + ClassData* getOrConstructClass(const string& name); + void registerBaseclasses(); + void registerDummyTypes(); + void registerMeta(); + void addConstants(PyObject* module); + void registerOperators(ClassData* cls); + void addParentMethods(ClassData* cls, ClassData* base); + WrapperRegistry(); + std::map<std::string, ClassData*> mClasses; + std::vector<ClassData*> mClassList; + std::vector<InitFunc> mExtInitializers; + std::vector<std::string> mPaths; + std::string mCode, mScriptName; + std::vector<std::string> args; +}; + +//****************************************************************************** +// Callback functions + +PyObject* cbGetClass(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cPureName); +} + +PyObject* cbGetTemplate(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cTemplate); +} + +PyObject* cbGetCName(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cName); +} + +void cbDealloc(PbObject* self) { + //cout << "dealloc " << self->instance->getName() << " " << self->classdef->cName << endl; + if (self->instance) { + // don't delete top-level objects + if (self->instance->getParent() != self->instance) + delete self->instance; + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyObject* cbNew(PyTypeObject *type, PyObject *args, PyObject *kwds) { + PbObject *self = (PbObject*) type->tp_alloc(type, 0); + if (self != NULL) { + // lookup and link classdef + self->classdef = WrapperRegistry::instance().lookup(type->tp_name); + self->instance = NULL; + //cout << "creating " << self->classdef->cName << endl; + } else + errMsg("can't allocate new python class object"); + return (PyObject*) self; +} + +int cbDisableConstructor(PyObject* self, PyObject* args, PyObject* kwds) { + errMsg("Can't instantiate a class template without template arguments"); + return -1; +} + + +PyMODINIT_FUNC PyInit_Main(void) { +#if PY_MAJOR_VERSION >= 3 + WrapperRegistry::instance().construct_lite(); + return WrapperRegistry::instance().initModule(); +#else + WrapperRegistry::instance().construct_lite(); + WrapperRegistry::instance().initModule(); +#endif +} + +PyMODINIT_FUNC PyInit_Main_Link(void) { +#if PY_MAJOR_VERSION >= 3 + return PyInit_Main(); +#else + PyInit_Main(); +#endif + +} + +PyObject *PyInit_Main_Obj(void) +{ + return PyInit_Main(); +} + +//****************************************************** +// WrapperRegistry + +WrapperRegistry::WrapperRegistry() { + addClass("__modclass__", "__modclass__" , ""); + addClass("PbClass", "PbClass", ""); +} + +ClassData* WrapperRegistry::getOrConstructClass(const string& classname) { + map<string,ClassData*>::iterator it = mClasses.find(classname); + + if (it != mClasses.end()) + return it->second; + ClassData* data = new ClassData; + data->cName = classname; + data->cPureName = classname; + data->cTemplate = ""; + size_t tplIdx = classname.find('<'); + if (tplIdx != string::npos) { + data->cPureName = classname.substr(0,tplIdx); + data->cTemplate = classname.substr(tplIdx+1, classname.find('>')-tplIdx-1); + } + data->baseclass = NULL; + data->constructor = cbDisableConstructor; + mClasses[classname] = data; + mClassList.push_back(data); + return data; +} + +void replaceAll(string& source, string const& find, string const& replace) { + for(string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;) + { + source.replace(i, find.length(), replace); + i += replace.length() - find.length() + 1; + } +} + +void WrapperRegistry::addClass(const string& pyName, const string& internalName, const string& baseclass) { + ClassData* data = getOrConstructClass(internalName); + + // regularize python name + string pythonName = pyName; + replaceAll(pythonName, "<", "_"); + replaceAll(pythonName, ">", ""); + replaceAll(pythonName, ",", "_"); + + if (data->pyName.empty()) + data->pyName = pythonName; + mClasses[pythonName] = data; + if (!baseclass.empty()) + data->baseclassName = baseclass; +} + +void WrapperRegistry::addExternalInitializer(InitFunc func) { + mExtInitializers.push_back(func); +} + +void WrapperRegistry::addPythonPath(const string& path) { + mPaths.push_back(path); +} + +void WrapperRegistry::addPythonCode(const string& file, const string& code) { + mCode += code + "\n"; +} + +void WrapperRegistry::addGetSet(const string& classname, const string& property, Getter getfunc, Setter setfunc) { + ClassData* classdef = getOrConstructClass(classname); + GetSet& def = classdef->getset[property]; + if (def.name.empty()) { + def.name = property; + def.doc = property; + } + if (getfunc) def.getter = getfunc; + if (setfunc) def.setter = setfunc; +} + +void WrapperRegistry::addMethod(const string& classname, const string& methodname, GenericFunction func) { + string aclass = classname; + if (aclass.empty()) + aclass = "__modclass__"; + + ClassData* classdef = getOrConstructClass(aclass); + for(int i=0; i<(int)classdef->methods.size(); i++) + if (classdef->methods[i].name == methodname) return; // avoid duplicates + classdef->methods.push_back(Method(methodname,methodname,func)); +} + +void WrapperRegistry::addOperator(const string& classname, const string& methodname, OperatorFunction func) { + if (classname.empty()) + errMsg("PYTHON operators have to be defined within classes."); + + string op = methodname.substr(8); + ClassData* classdef = getOrConstructClass(classname); + classdef->ops[op] = func; +} + +void WrapperRegistry::addConstructor(const string& classname, Constructor func) { + ClassData* classdef = getOrConstructClass(classname); + classdef->constructor = func; +} + +void WrapperRegistry::addParentMethods(ClassData* cur, ClassData* base) { + if (base == 0) return; + + for (vector<Method>::iterator it = base->methods.begin(); it != base->methods.end(); ++it) + addMethod(cur->cName, it->name, it->func); + + for (map<string,GetSet>::iterator it = base->getset.begin(); it != base->getset.end(); ++it) + addGetSet(cur->cName, it->first, it->second.getter, it->second.setter); + + for (map<string,OperatorFunction>::iterator it = base->ops.begin(); it != base->ops.end(); ++it) + cur->ops[it->first] = it->second; + + addParentMethods(cur, base->baseclass); +} + +void WrapperRegistry::registerBaseclasses() { + for (int i=0; i<(int)mClassList.size(); i++) { + string bname = mClassList[i]->baseclassName; + if(!bname.empty()) { + mClassList[i]->baseclass = lookup(bname); + if (!mClassList[i]->baseclass) + errMsg("Registering class '" + mClassList[i]->cName + "' : Base class '" + bname + "' not found"); + } + } + + for (int i=0; i<(int)mClassList.size(); i++) { + addParentMethods(mClassList[i], mClassList[i]->baseclass); + } +} + +void WrapperRegistry::registerMeta() { + for (int i=0; i<(int)mClassList.size(); i++) { + mClassList[i]->getset["_class"] = GetSet("_class", "C class name", (Getter)cbGetClass, 0); + mClassList[i]->getset["_cname"] = GetSet("_cname", "Full C name", (Getter)cbGetCName, 0); + mClassList[i]->getset["_T"] = GetSet("_T", "C template argument", (Getter)cbGetTemplate, 0); + } +} + +void WrapperRegistry::registerOperators(ClassData* cls) { + PyNumberMethods& num = cls->numInfo; + for (map<string,OperatorFunction>::iterator it = cls->ops.begin(); it != cls->ops.end(); it++) { + const string& op = it->first; + OperatorFunction func = it->second; + if (op=="+=") num.nb_inplace_add = func; + else if (op=="-=") num.nb_inplace_subtract = func; + else if (op=="*=") num.nb_inplace_multiply = func; + else if (op=="+") num.nb_add = func; + else if (op=="-") num.nb_subtract = func; + else if (op=="*") num.nb_multiply = func; +# if PY_MAJOR_VERSION < 3 + else if (op=="/=") num.nb_inplace_divide = func; + else if (op=="/") num.nb_divide = func; +# else + else if (op=="/=") num.nb_inplace_true_divide = func; + else if (op=="/") num.nb_true_divide = func; +# endif + else + errMsg("PYTHON operator " + op + " not supported"); + } +} + +void WrapperRegistry::registerDummyTypes() { + vector<string> add; + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + string cName = (*it)->cName; + if (cName.find('<') != string::npos) + add.push_back(cName.substr(0,cName.find('<'))); + } + for (int i=0; i<(int)add.size(); i++) + addClass(add[i],add[i],""); +} + +ClassData* WrapperRegistry::lookup(const string& name) { + for(map<string, ClassData*>::iterator it = mClasses.begin(); it != mClasses.end(); ++it) { + if (it->first == name || it->second->cName == name) + return it->second; + } + return NULL; +} + +void WrapperRegistry::cleanup() { + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + delete *it; + } + mClasses.clear(); + mClassList.clear(); +} + +WrapperRegistry& WrapperRegistry::instance() { + static WrapperRegistry inst; + return inst; +} + +bool WrapperRegistry::canConvert(ClassData* from, ClassData* to) { + if (from == to) return true; + if (from->baseclass) + return canConvert(from->baseclass, to); + return false; +} + +void WrapperRegistry::addConstants(PyObject* module) { + // expose arguments + PyObject* list = PyList_New(args.size()); + for (int i=0; i<(int)args.size(); i++) + PyList_SET_ITEM(list,i,Manta::toPy(args[i])); + PyModule_AddObject(module, "args", list); + PyModule_AddObject(module,"CUDA",Manta::toPy(mScriptName)); + + // expose compile flags +#ifdef CUDA + PyModule_AddObject(module,"CUDA",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"CUDA",Manta::toPy<bool>(false)); +#endif +#ifdef DEBUG + PyModule_AddObject(module,"DEBUG",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"DEBUG",Manta::toPy<bool>(false)); +#endif +#ifdef MT + PyModule_AddObject(module,"MT",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"MT",Manta::toPy<bool>(false)); +#endif +#ifdef GUI + PyModule_AddObject(module,"GUI",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"GUI",Manta::toPy<bool>(false)); +#endif +} + +void WrapperRegistry::runPreInit() { + // add python directories to path + PyObject *sys_path = PySys_GetObject((char*)"path"); + for (size_t i=0; i<mPaths.size(); i++) { + PyObject *path = Manta::toPy(mPaths[i]); + if (sys_path == NULL || path == NULL || PyList_Append(sys_path, path) < 0) { + errMsg("unable to set python path"); + } + Py_DECREF(path); + } + if (!mCode.empty()) { + mCode = "from manta import *\n" + mCode; + PyRun_SimpleString(mCode.c_str()); + } +} + +PyObject* WrapperRegistry::createPyObject(const string& classname, const string& name, Manta::PbArgs& args, Manta::PbClass *parent) { + ClassData* classdef = lookup(classname); + if (!classdef) + errMsg("Class " + classname + " doesn't exist."); + + // create object + PyObject* obj = cbNew(&classdef->typeInfo, NULL, NULL); + PbObject* self = (PbObject*)obj; + PyObject* nkw = 0; + + if (args.kwds()) + nkw = PyDict_Copy(args.kwds()); + else + nkw = PyDict_New(); + + PyObject* nocheck = Py_BuildValue("s","yes"); + PyDict_SetItemString(nkw, "nocheck", nocheck); + if (parent) PyDict_SetItemString(nkw, "parent", parent->getPyObject()); + + // create instance + if (self->classdef->constructor(obj, args.linArgs(), nkw) < 0) + errMsg("error raised in constructor"); // assume condition is already set + + Py_DECREF(nkw); + Py_DECREF(nocheck); + self->instance->setName(name); + + return obj; +} + +// prepare typeinfo and register python module +void WrapperRegistry::construct(const string& scriptname, const vector<string>& args) { + mScriptName = scriptname; + this->args = args; + + registerBaseclasses(); + registerMeta(); + registerDummyTypes(); + + // load main extension module + PyImport_AppendInittab((char*)gDefaultModuleName.c_str(), PyInit_Main); +} +void WrapperRegistry::addScriptData(const std::string &scriptname, const vector<string> &args) +{ + mScriptName = scriptname; + this->args = args; +} + +void WrapperRegistry::construct_lite() { + registerBaseclasses(); + registerMeta(); + registerDummyTypes(); +} + +inline PyObject* castPy(PyTypeObject* p) { + return reinterpret_cast<PyObject*>(static_cast<void*>(p)); +} + +PyObject* WrapperRegistry::initModule() { + // generate and terminate all method lists + PyMethodDef sentinelFunc = { NULL, NULL, 0, NULL }; + PyGetSetDef sentinelGetSet = { NULL, NULL, NULL, NULL, NULL }; + for (int i=0; i<(int)mClassList.size(); i++) { + ClassData* cls = mClassList[i]; + cls->genMethods.clear(); + cls->genGetSet.clear(); + for (vector<Method>::iterator i2 = cls->methods.begin(); i2 != cls->methods.end(); ++i2) + cls->genMethods.push_back(i2->def()); + for (map<string,GetSet>::iterator i2 = cls->getset.begin(); i2 != cls->getset.end(); ++i2) + cls->genGetSet.push_back(i2->second.def()); + + cls->genMethods.push_back(sentinelFunc); + cls->genGetSet.push_back(sentinelGetSet); + } + + // prepare module info +#if PY_MAJOR_VERSION >= 3 + static PyModuleDef MainModule = { + PyModuleDef_HEAD_INIT, + gDefaultModuleName.c_str(), + "Bridge module to the C++ solver", + -1, + NULL, NULL, NULL, NULL, NULL + }; + // get generic methods (plugin functions) + MainModule.m_methods = &mClasses["__modclass__"]->genMethods[0]; + + // create module + PyObject* module = PyModule_Create(&MainModule); +#else + PyObject* module = Py_InitModule(gDefaultModuleName.c_str(), &mClasses["__modclass__"]->genMethods[0]); +#endif + if (module == NULL) + return NULL; + + // load classes + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + ClassData& data = **it; + char* nameptr = (char*)data.pyName.c_str(); + + // define numeric substruct + PyNumberMethods* num = 0; + if (!data.ops.empty()) { + num = &data.numInfo; + memset(num,0,sizeof(PyNumberMethods)); + registerOperators(&data); + } + + // define python classinfo + PyTypeObject t = { + PyVarObject_HEAD_INIT(NULL, 0) + (char*)data.pyName.c_str(),// tp_name + sizeof(PbObject), // tp_basicsize + 0, // tp_itemsize + (destructor)cbDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_reserved + 0, // tp_repr + num, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, // tp_flags + nameptr, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + &data.genMethods[0], // tp_methods + 0, // tp_members + &data.genGetSet[0], // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)(data.constructor),// tp_init + 0, // tp_alloc + cbNew // tp_new + }; + data.typeInfo = t; + + if (PyType_Ready(&data.typeInfo) < 0) + continue; + + for(map<string,ClassData*>::iterator i2 = mClasses.begin(); i2 != mClasses.end(); ++i2) { + if (*it != i2->second) continue; + // register all aliases + Py_INCREF(castPy(&data.typeInfo)); + PyModule_AddObject(module, (char*)i2->first.c_str(), (PyObject*) &data.typeInfo); + } + } + + // externals + for(vector<InitFunc>::iterator it = mExtInitializers.begin(); it != mExtInitializers.end(); ++it) { + (*it)(module); + } + + addConstants(module); + + return module; +} + + +//****************************************************** +// Register members and exposed functions + +void setup(const std::string& filename, const std::vector<std::string>& args) { +// WrapperRegistry::instance().construct(filename,args); +// Py_Initialize(); + WrapperRegistry::instance().addScriptData(filename, args); + WrapperRegistry::instance().runPreInit(); +} + +void finalize() { +// Py_Finalize(); + WrapperRegistry::instance().cleanup(); +} + +bool canConvert(PyObject* obj, const string& classname) { + ClassData* from = ((PbObject*)obj)->classdef; + ClassData* dest = WrapperRegistry::instance().lookup(classname); + if (!dest) + errMsg("Classname '" + classname + "' is not registered."); + return WrapperRegistry::instance().canConvert(from, dest); +} + +Manta::PbClass* objFromPy(PyObject* obj) { + if (Py_TYPE(obj)->tp_dealloc != (destructor)cbDealloc) // not a manta object + return NULL; + + return ((PbObject*) obj)->instance; +} + +PyObject* copyObject(Manta::PbClass* cls, const string& classname) { + ClassData* classdef = WrapperRegistry::instance().lookup(classname); + assertMsg(classdef,"python class " + classname + " does not exist."); + + // allocate new object + PbObject *obj = (PbObject*) classdef->typeInfo.tp_alloc(&(classdef->typeInfo), 0); + assertMsg(obj, "cannot allocate new python object"); + + obj->classdef = classdef; + cls->registerObject((PyObject*)obj, 0); + + return cls->getPyObject(); +} + +Manta::PbClass* createPy(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass* parent) { + PyObject* obj = WrapperRegistry::instance().createPyObject(classname, name, args, parent); + return ((PbObject*)obj)->instance; +} + +void setReference(Manta::PbClass* cls, PyObject* obj) { + ((PbObject*) obj)->instance = cls; +} + +Register::Register(const string& className, const string& funcName, GenericFunction func) { + WrapperRegistry::instance().addMethod(className, funcName, func); +} +Register::Register(const string& className, const string& funcName, OperatorFunction func) { + WrapperRegistry::instance().addOperator(className, funcName, func); +} +Register::Register(const string& className, const string& funcName, Constructor func) { + WrapperRegistry::instance().addConstructor(className, func); +} +Register::Register(const string& className, const string& property, Getter getter, Setter setter) { + WrapperRegistry::instance().addGetSet(className, property, getter, setter); +} +Register::Register(const string& className, const string& pyName, const string& baseClass) { + WrapperRegistry::instance().addClass(pyName, className, baseClass); +} +Register::Register(const string& file, const string& pythonCode) { + WrapperRegistry::instance().addPythonCode(file, pythonCode); +} +Register::Register(InitFunc func) { + WrapperRegistry::instance().addExternalInitializer(func); +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/pwrapper/registry.h b/source/blender/python/manta_full/source/pwrapper/registry.h new file mode 100644 index 00000000000..4be636b01a4 --- /dev/null +++ b/source/blender/python/manta_full/source/pwrapper/registry.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Auto python registry + * + ******************************************************************************/ + +#ifndef _REGISTRY_H +#define _REGISTRY_H + +#include <string> +#include <vector> + +// forward declaration to minimize Python.h includes +#ifndef PyObject_HEAD +#ifndef PyObject_Fake +struct _object; +typedef _object PyObject; +#define PyObject_Fake +#endif +#endif + +namespace Manta { + class PbClass; + class PbArgs; +} + +// ************************************************** +// NOTE +// Everything in this file is intend only for internal +// use by the generated wrappers or pclass/pconvert. +// For user code, use the functionality exposed in +// pclass.h / pconvert.h instead. +// ************************************************** + +// Used to turn names into strings +namespace Manta { +template<class T> struct Namify { + static const char* S; +}; +} +namespace Pb { + +// internal registry access +void setup(const std::string& filename, const std::vector<std::string>& args); +void finalize(); +bool canConvert(PyObject* obj, const std::string& to); +Manta::PbClass* objFromPy(PyObject* obj); +Manta::PbClass* createPy(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass* parent); +void setReference(Manta::PbClass* cls, PyObject* obj); +PyObject* copyObject(Manta::PbClass* cls, const std::string& classname); + +// callback type +typedef void (*InitFunc)(PyObject*); +PyObject *PyInit_Main_Obj(void); +typedef PyObject* (*GenericFunction)(PyObject* self, PyObject* args, PyObject* kwds); +typedef PyObject* (*OperatorFunction)(PyObject* self, PyObject* o); +typedef int (*Constructor)(PyObject* self, PyObject* args, PyObject* kwds); +typedef PyObject* (*Getter)(PyObject* self, void* closure); +typedef int (*Setter)(PyObject* self, PyObject* value, void* closure); + +//! Auto registry of python methods and classes +struct Register { + //! register method + Register(const std::string& className, const std::string& funcName, GenericFunction func); + //! register operator + Register(const std::string& className, const std::string& funcName, OperatorFunction func); + //! register constructor + Register(const std::string& className, const std::string& funcName, Constructor func); + //! register getter/setter + Register(const std::string& className, const std::string& property, Getter getter, Setter setter); + //! register class + Register(const std::string& className, const std::string& pyName, const std::string& baseClass); + //! register python code + Register(const std::string& file, const std::string& pythonCode); + //! register external code + Register(InitFunc func); +}; + +} +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/python/defines.py b/source/blender/python/manta_full/source/python/defines.py new file mode 100644 index 00000000000..6bf5ee0ef57 --- /dev/null +++ b/source/blender/python/manta_full/source/python/defines.py @@ -0,0 +1,27 @@ +################################################################################ +# +# MantaFlow fluid solver framework +# Copyright 2011 Tobias Pfaff, Nils Thuerey +# +# This program is free software, distributed under the terms of the +# GNU General Public License (GPL) +# http://www.gnu.org/licenses +# +# Defines some constants for use in python subprograms +# +################################################################################# + +# mantaflow conventions +Real = float + +# grid flags +FlagFluid = 1 +FlagObstacle = 2 +FlagEmpty = 4 +FlagStick = 128 +FlagReserved = 256 + +# integration mode +IntEuler = 0 +IntRK2 = 1 +IntRK4 = 2 diff --git a/source/blender/python/manta_full/source/shapes.cpp b/source/blender/python/manta_full/source/shapes.cpp new file mode 100644 index 00000000000..eaba1f3fda0 --- /dev/null +++ b/source/blender/python/manta_full/source/shapes.cpp @@ -0,0 +1,381 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Shape classes + * + ******************************************************************************/ + +#include "shapes.h" +#include "commonkernels.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// Shape class members + +Shape::Shape (FluidSolver* parent) + : PbClass(parent), mType(TypeNone) +{ +} + +LevelsetGrid Shape::computeLevelset() { + // note - 3d check deactivated! TODO double check... + LevelsetGrid phi(getParent()); + generateLevelset(phi); + return phi; +} + +bool Shape::isInside(const Vec3& pos) const { + return false; +} + +//! Kernel: Apply a shape to a grid, setting value inside +KERNEL template<class T> +void ApplyShapeToGrid (Grid<T>* grid, Shape* shape, T value, FlagGrid* respectFlags) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (shape->isInsideGrid(i,j,k)) + (*grid)(i,j,k) = value; +} + +//! Kernel: Apply a shape to a grid, setting value inside (scaling by SDF value) +KERNEL template<class T> +void ApplyShapeToGridSmooth (Grid<T>* grid, Grid<Real>& phi, Real sigma, Real shift, T value, FlagGrid* respectFlags) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + const Real p = phi(i,j,k) - shift; + if (p < -sigma) + (*grid)(i,j,k) = value; + else if (p < sigma) + (*grid)(i,j,k) = value*(0.5f*(1.0f-p/sigma)); +} + +//! Kernel: Apply a shape to a MAC grid, setting value inside +KERNEL void ApplyShapeToMACGrid (MACGrid* grid, Shape* shape, Vec3 value, FlagGrid* respectFlags) +{ + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (shape->isInside(Vec3(i,j+0.5,k+0.5))) (*grid)(i,j,k).x = value.x; + if (shape->isInside(Vec3(i+0.5,j,k+0.5))) (*grid)(i,j,k).y = value.y; + if (shape->isInside(Vec3(i+0.5,j+0.5,k))) (*grid)(i,j,k).z = value.z; +} + +void Shape::applyToGrid(GridBase* grid, FlagGrid* respectFlags) { + if (grid->getType() & GridBase::TypeInt) + ApplyShapeToGrid<int> ((Grid<int>*)grid, this, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyShapeToGrid<Real> ((Grid<Real>*)grid, this, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeMAC) + ApplyShapeToMACGrid ((MACGrid*)grid, this, _args.get<Vec3>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyShapeToGrid<Vec3> ((Grid<Vec3>*)grid, this, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGrid(): unknown grid type"); +} + +void Shape::applyToGridSmooth(GridBase* grid, Real sigma, Real shift, FlagGrid* respectFlags) { + Grid<Real> phi(grid->getParent()); + generateLevelset(phi); + + if (grid->getType() & GridBase::TypeInt) + ApplyShapeToGridSmooth<int> ((Grid<int>*)grid, phi, sigma, shift, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyShapeToGridSmooth<Real> ((Grid<Real>*)grid, phi, sigma, shift, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyShapeToGridSmooth<Vec3> ((Grid<Vec3>*)grid, phi, sigma, shift, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGridSmooth(): unknown grid type"); +} + +void Shape::collideMesh(Mesh& mesh) { + const Real margin = 0.2; + + Grid<Real> phi(getParent()); + Grid<Vec3> grad(getParent()); + generateLevelset(phi); + GradientOp(grad, phi); + + const int num=mesh.numNodes(); + for(int i=0; i<num; i++) { + const Vec3& p = mesh.nodes(i).pos; + mesh.nodes(i).flags &= ~(Mesh::NfCollide | Mesh::NfMarked); + if (!phi.isInBounds(p,1)) continue; + + for (int iter=0; iter<10; iter++) { + const Real dist= phi.getInterpolated(p); + if (dist<margin) { + Vec3 n = grad.getInterpolated(p); + normalize(n); + mesh.nodes(i).pos += (margin-dist) * n; + mesh.nodes(i).flags |= Mesh::NfCollide | Mesh::NfMarked; + } + else break; + } + } +} + +//****************************************************************************** +// Derived shape class members + +Box::Box(FluidSolver* parent, Vec3 center, Vec3 p0, Vec3 p1, Vec3 size) + : Shape(parent) +{ + mType = TypeBox; + if (center.isValid() && size.isValid()) { + mP0 = center - size; + mP1 = center + size; + } else if (p0.isValid() && p1.isValid()) { + mP0 = p0; + mP1 = p1; + } else + errMsg("Box: specify either p0,p1 or size,center"); + +} + +bool Box::isInside(const Vec3& pos) const { + return (pos.x >= mP0.x && pos.y >= mP0.y && pos.z >= mP0.z && + pos.x <= mP1.x && pos.y <= mP1.y && pos.z <= mP1.z); +} + +void Box::generateMesh(Mesh* mesh) { + const int quadidx[24] = { 0,4,6,2, 3,7,5,1, 0,1,5,4, 6,7,3,2, 0,2,3,1, 5,7,6,4 }; + const int nodebase = mesh->numNodes(); + int oldtri = mesh->numTris(); + for (int i=0; i<8; i++) { + Node p; + p.flags = 0; + p.pos = mP0; + if (i&1) p.pos.x=mP1.x; + if (i&2) p.pos.y=mP1.y; + if (i&4) p.pos.z=mP1.z; + mesh->addNode(p); + } + for (int i=0; i<6; i++) { + mesh->addTri(Triangle(nodebase + quadidx[i*4+0], nodebase + quadidx[i*4+1], nodebase + quadidx[i*4+3])); + mesh->addTri(Triangle(nodebase + quadidx[i*4+1], nodebase + quadidx[i*4+2], nodebase + quadidx[i*4+3])); + } + mesh->rebuildCorners(oldtri,-1); + mesh->rebuildLookup(oldtri,-1); +} + +//! Kernel: Analytic SDF for box shape +KERNEL void BoxSDF(Grid<Real>& phi, const Vec3& p1, const Vec3& p2) { + const Vec3 p(i+0.5, j+0.5, k+0.5); + if (p.x <= p2.x && p.x >= p1.x && p.y <= p2.y && p.y >= p1.y && p.z <= p2.z && p.z >= p1.z) { + // inside: minimal surface distance + Real mx = max(p.x-p2.x, p1.x-p.x); + Real my = max(p.y-p2.y, p1.y-p.y); + Real mz = max(p.z-p2.z, p1.z-p.z); + phi(i,j,k) = max(mx,max(my,mz)); + } else if (p.y <= p2.y && p.y >= p1.y && p.z <= p2.z && p.z >= p1.z) { + // outside plane X + phi(i,j,k) = max(p.x-p2.x, p1.x-p.x); + } else if (p.x <= p2.x && p.x >= p1.x && p.z <= p2.z && p.z >= p1.z) { + // outside plane Y + phi(i,j,k) = max(p.y-p2.y, p1.y-p.y); + } else if (p.x <= p2.x && p.x >= p1.x && p.y <= p2.y && p.y >= p1.y) { + // outside plane Z + phi(i,j,k) = max(p.z-p2.z, p1.z-p.z); + } else if (p.x > p1.x && p.x < p2.x) { + // lines X + Real m1 = sqrt(square(p1.y-p.y)+square(p1.z-p.z)); + Real m2 = sqrt(square(p2.y-p.y)+square(p1.z-p.z)); + Real m3 = sqrt(square(p1.y-p.y)+square(p2.z-p.z)); + Real m4 = sqrt(square(p2.y-p.y)+square(p2.z-p.z)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else if (p.y > p1.y && p.y < p2.y) { + // lines Y + Real m1 = sqrt(square(p1.x-p.x)+square(p1.z-p.z)); + Real m2 = sqrt(square(p2.x-p.x)+square(p1.z-p.z)); + Real m3 = sqrt(square(p1.x-p.x)+square(p2.z-p.z)); + Real m4 = sqrt(square(p2.x-p.x)+square(p2.z-p.z)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else if (p.z > p1.x && p.z < p2.z) { + // lines Z + Real m1 = sqrt(square(p1.y-p.y)+square(p1.x-p.x)); + Real m2 = sqrt(square(p2.y-p.y)+square(p1.x-p.x)); + Real m3 = sqrt(square(p1.y-p.y)+square(p2.x-p.x)); + Real m4 = sqrt(square(p2.y-p.y)+square(p2.x-p.x)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else { + // points + Real m = norm(p-Vec3(p1.x,p1.y,p1.z)); + m = min(m, norm(p-Vec3(p1.x,p1.y,p2.z))); + m = min(m, norm(p-Vec3(p1.x,p2.y,p1.z))); + m = min(m, norm(p-Vec3(p1.x,p2.y,p2.z))); + m = min(m, norm(p-Vec3(p2.x,p1.y,p1.z))); + m = min(m, norm(p-Vec3(p2.x,p1.y,p2.z))); + m = min(m, norm(p-Vec3(p2.x,p2.y,p1.z))); + m = min(m, norm(p-Vec3(p2.x,p2.y,p2.z))); + phi(i,j,k) = m; + } +} +void Box::generateLevelset(Grid<Real>& phi) { + BoxSDF(phi, mP0, mP1); +} + +Sphere::Sphere (FluidSolver* parent, Vec3 center, Real radius, Vec3 scale) + : Shape(parent), mCenter(center), mScale(scale), mRadius(radius) +{ + mType = TypeSphere; +} + +bool Sphere::isInside(const Vec3& pos) const { + return normSquare((pos - mCenter) / mScale) <= mRadius * mRadius; +} + +struct Tri { Vec3 t[3]; int i[3]; Tri(Vec3 a,Vec3 b, Vec3 c) {t[0]=a;t[1]=b;t[2]=c;}}; +void Sphere::generateMesh(Mesh* mesh) { + vector<Tri> tris; + const int iterations = 3; + int oldtri = mesh->numTris(); + + // start with octahedron + const Real d = sqrt(0.5); + Vec3 p[6] = {Vec3(0,1,0), Vec3(0,-1,0), Vec3(-d,0,-d), Vec3(d,0,-d), Vec3(d,0,d), Vec3(-d,0,d)}; + tris.push_back(Tri(p[0],p[4],p[3])); + tris.push_back(Tri(p[0],p[5],p[4])); + tris.push_back(Tri(p[0],p[2],p[5])); + tris.push_back(Tri(p[0],p[3],p[2])); + tris.push_back(Tri(p[1],p[3],p[4])); + tris.push_back(Tri(p[1],p[4],p[5])); + tris.push_back(Tri(p[1],p[5],p[2])); + tris.push_back(Tri(p[1],p[2],p[3])); + + // Bisect each edge and move to the surface of a unit sphere + for (int it=0; it<iterations; it++) { + int ntold = tris.size(); + for (int i=0; i<ntold; i++) { + Vec3 pa = 0.5 * (tris[i].t[0] + tris[i].t[1]); + Vec3 pb = 0.5 * (tris[i].t[1] + tris[i].t[2]); + Vec3 pc = 0.5 * (tris[i].t[2] + tris[i].t[0]); + normalize(pa); normalize(pb); normalize(pc); + + tris.push_back(Tri(tris[i].t[0], pa, pc)); + tris.push_back(Tri(pa, tris[i].t[1], pb)); + tris.push_back(Tri(pb, tris[i].t[2], pc)); + tris[i].t[0] = pa; + tris[i].t[1] = pb; + tris[i].t[2] = pc; + } + } + + // index + scale + vector<Vec3> nodes; + for (size_t i=0; i<tris.size(); i++) { + for (int t=0; t<3; t++) { + Vec3 p = mCenter + tris[i].t[t] * mRadius * mScale; + // vector already there ? + int idx=nodes.size(); + for (size_t j=0; j<nodes.size(); j++) { + if (p==nodes[j]) { + idx = j; break; + } + } + if (idx == (int)nodes.size()) + nodes.push_back(p); + tris[i].i[t] = idx; + } + } + + // add the to mesh + const int ni = mesh->numNodes(); + for (size_t i=0; i<nodes.size(); i++) { + mesh->addNode(Node(nodes[i]));} + for (size_t t=0; t<tris.size(); t++) + mesh->addTri(Triangle(tris[t].i[0]+ni, tris[t].i[1]+ni, tris[t].i[2]+ni)); + + mesh->rebuildCorners(oldtri,-1); + mesh->rebuildLookup(oldtri,-1); +} + +KERNEL void SphereSDF(Grid<Real>& phi, Vec3 center, Real radius, Vec3 scale) { + phi(i,j,k) = norm((Vec3(i+0.5,j+0.5,k+0.5)-center)/scale)-radius; +} +void Sphere::generateLevelset(Grid<Real>& phi) { + SphereSDF(phi, mCenter, mRadius, mScale); +} + +Cylinder::Cylinder(FluidSolver* parent, Vec3 center, Real radius, Vec3 z) + : Shape(parent), mCenter(center), mRadius(radius) +{ + mType = TypeCylinder; + mZDir = z; + mZ = normalize(mZDir); +} + +bool Cylinder::isInside(const Vec3& pos) const { + Real z = dot(pos-mCenter, mZDir); + if (fabs(z) > mZ) return false; + Real r2 = normSquare(pos-mCenter)-square(z); + return r2 < square(mRadius); +} + +void Cylinder::generateMesh(Mesh* mesh) { + // generate coordinate system + Vec3 x = getOrthogonalVector(mZDir)*mRadius; + Vec3 y = cross(x, mZDir); + Vec3 z = mZDir*mZ; + int oldtri = mesh->numTris(); + + // construct node ring + const int N = 20; + const int base = mesh->numNodes(); + for (int i=0;i<N;i++) { + const Real phi = 2.0*M_PI*(Real)i/(Real)N; + Vec3 r = x*cos(phi) + y*sin(phi) + mCenter; + mesh->addNode(Node(r+z)); + mesh->addNode(Node(r-z)); + } + // top/bottom center + mesh->addNode(Node(mCenter+z)); + mesh->addNode(Node(mCenter-z)); + + // connect with tris + for (int i=0;i<N;i++) { + int cur = base+2*i; + int next = base+2*((i+1)%N); + // outside + mesh->addTri(Triangle(cur, next, cur+1)); + mesh->addTri(Triangle(next, next+1, cur+1)); + // upper / lower + mesh->addTri(Triangle(cur,base+2*N,next)); + mesh->addTri(Triangle(cur+1,next+1,base+2*N+1)); + } + + mesh->rebuildCorners(oldtri, -1); + mesh->rebuildLookup(oldtri,-1); +} + +KERNEL void +CylinderSDF(Grid<Real>& phi, Vec3 center, Real radius, Vec3 zaxis, Real maxz) { + Vec3 p=Vec3(i+0.5,j+0.5,k+0.5)-center; + Real z = fabs(dot(p, zaxis)); + Real r = sqrt(normSquare(p)-z*z); + if (z < maxz) { + // cylinder z area + if (r < radius) + phi(i,j,k) = max(r-radius,z-maxz); + else + phi(i,j,k) = r-radius; + } else if (r < radius) { + // cylinder top area + phi(i,j,k) = fabs(z-maxz); + } else { + // edge + phi(i,j,k) = sqrt(square(z-maxz)+square(r-radius)); + } +} +void Cylinder::generateLevelset(Grid<Real>& phi) { + CylinderSDF(phi, mCenter, mRadius, mZDir, mZ); +} + +} //namespace diff --git a/source/blender/python/manta_full/source/shapes.h b/source/blender/python/manta_full/source/shapes.h new file mode 100644 index 00000000000..0f5ac32dcae --- /dev/null +++ b/source/blender/python/manta_full/source/shapes.h @@ -0,0 +1,130 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * shapes classes + * + ******************************************************************************/ + +#ifndef _SHAPES_H +#define _SHAPES_H + +#include "manta.h" +#include "vectorbase.h" +#include "levelset.h" + +namespace Manta { + +// forward declaration +class Mesh; + +//! Base class for all shapes +PYTHON class Shape : public PbClass { +public: + enum GridType { TypeNone = 0, TypeBox = 1, TypeSphere = 2, TypeCylinder }; + + PYTHON Shape(FluidSolver* parent); + + //! Get the type of grid + inline GridType getType() const { return mType; } + + //! Apply shape to flag grid, set inside cells to <value> + PYTHON void applyToGrid(GridBase* grid, FlagGrid* respectFlags=0); + PYTHON void applyToGridSmooth(GridBase* grid, Real sigma=1.0, Real shift=0, FlagGrid* respectFlags=0); + PYTHON LevelsetGrid computeLevelset(); + PYTHON void collideMesh(Mesh& mesh); + PYTHON virtual Vec3 getCenter() const { return Vec3::Zero; } + PYTHON virtual void setCenter(const Vec3& center) {} + PYTHON virtual Vec3 getExtent() const { return Vec3::Zero; } + + //! Inside test of the shape + virtual bool isInside(const Vec3& pos) const; + inline bool isInsideGrid(int i, int j, int k) const { return isInside(Vec3(i+0.5,j+0.5,k+0.5)); }; + + virtual void generateMesh(Mesh* mesh) {} ; + virtual void generateLevelset(Grid<Real>& phi) {}; + +protected: + GridType mType; +}; + +//! Dummy shape +PYTHON class NullShape : public Shape { +public: + PYTHON NullShape (FluidSolver* parent) : Shape(parent) {} + + virtual bool isInside(const Vec3& pos) const { return false; } + virtual void generateMesh(Mesh* mesh) {} + +protected: + virtual void generateLevelset(Grid<Real>& phi) { gridSetConst<Real>( phi , 1000.0f ); } +}; + +//! Box shape +PYTHON class Box : public Shape { +public: + PYTHON Box(FluidSolver* parent, Vec3 center = Vec3::Invalid, Vec3 p0 = Vec3::Invalid, Vec3 p1 = Vec3::Invalid, Vec3 size = Vec3::Invalid); + + inline Vec3 getSize() const { return mP1-mP0; } + inline Vec3 getP0() const { return mP0; } + inline Vec3 getP1() const { return mP1; } + virtual void setCenter(const Vec3& center) { Vec3 dh=0.5*(mP1-mP0); mP0 = center-dh; mP1 = center+dh;} + virtual Vec3 getCenter() const { return 0.5*(mP1+mP0); } + virtual Vec3 getExtent() const { return getSize(); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: + Vec3 mP0, mP1; +}; + +//! Spherical shape +PYTHON class Sphere : public Shape { +public: + PYTHON Sphere (FluidSolver* parent, Vec3 center, Real radius, Vec3 scale=Vec3(1,1,1)); + + virtual void setCenter(const Vec3& center) { mCenter = center; } + virtual Vec3 getCenter() const { return mCenter; } + inline Real getRadius() const { return mRadius; } + virtual Vec3 getExtent() const { return Vec3(2.0*mRadius); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: + Vec3 mCenter, mScale; + Real mRadius; +}; + +//! Cylindrical shape +PYTHON class Cylinder : public Shape { +public: + PYTHON Cylinder (FluidSolver* parent, Vec3 center, Real radius, Vec3 z); + + PYTHON void setRadius(Real r) { mRadius = r; } + PYTHON void setZ(Vec3 z) { mZDir=z; mZ=normalize(mZDir); } + + virtual void setCenter(const Vec3& center) { mCenter=center; } + virtual Vec3 getCenter() const { return mCenter; } + inline Real getRadius() const { return mRadius; } + inline Vec3 getZ() const { return mZ*mZDir; } + virtual Vec3 getExtent() const { return Vec3(2.0*sqrt(square(mZ)+square(mRadius))); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: + Vec3 mCenter, mZDir; + Real mRadius, mZ; +}; + + + +} //namespace +#endif diff --git a/source/blender/python/manta_full/source/test.cpp b/source/blender/python/manta_full/source/test.cpp new file mode 100644 index 00000000000..c08c8b2fdac --- /dev/null +++ b/source/blender/python/manta_full/source/test.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Use this file to test new functionality + * + ******************************************************************************/ + +#include "levelset.h" +#include "commonkernels.h" +#include "particle.h" +#include <cmath> + +using namespace std; + +namespace Manta { + + +PYTHON template<class S> +void addToGrid(Grid<S>& a, S v) { + FOR_IDX(a) a[idx] += v; +} +PYTHON instantiate addToGrid<int>, addToGrid<Real>, addToGrid<Vec3>; + +//! Kernel: get component (not shifted) +/*KERNEL(idx) returns(Grid<Real> ret(parent)) +Grid<Real> GetComponent2(const Grid<Vec3>& grid, int dim) { + ret[idx] = grid[idx][dim]; +}; + +PYTHON void testp(Grid<Vec3>& b) { + Grid<Real> d(b.getParent()); + b(20,20,20) = Vec3(21,22,23); + { + cout <<"middle" << endl; + Grid<Real> a = GetComponent2(b,0); + cout << a(20,20,20) << endl; + cout <<"middle" << endl; + } + cout << "end" << endl;errMsg("f"); +} +*/ + +KERNEL(idx, reduce=+) returns (double sum=0) +double ddtest(const Grid<Real>& v) +{ + sum += v[idx]; +} + +KERNEL(idx, reduce=min) returns (double sum=0) +double detest(const Grid<Real>& v) +{ + if (sum < v[idx]) + sum = v[idx]; +} + +PYTHON void checkGrids(Grid<int>& flags1, Grid<int>& flags2, Grid<Real>& phi1, Grid<Real>& phi2, Grid<Vec3>& vel1, Grid<Vec3>& vel2) { + FOR_IJK(flags1) { + assertMsg(flags1(i,j,k) == flags2(i,j,k), "flags mismatch"); + assertMsg(norm(vel1(i,j,k)-vel2(i,j,k)) < 1e-1, "vel mismatch"); + assertMsg( fabs(phi1(i,j,k)-phi2(i,j,k)) < 1e-4, "phi mismatch"); + } +} + + +struct myvec { + myvec(int n) : x(n) { cout << "constructor" << endl; }; + myvec(const myvec& a) : x(a.x) { cout << "copy constructor" << endl; } + myvec& operator=(const myvec& a) { x=a.x; cout << "copy operator" << endl; return *this;} + int& operator[](int idx) { return x[idx]; } + + vector<int> x; +}; + +KERNEL(pts) returns(myvec vec(size)) +myvec testy(vector<int>& a) { + vec[idx] = a[idx]; +} + +PYTHON void kernelTest() { + cout << "kernel test" << endl; + vector<int> a(10); + for (int i=0;i<10;i++) a[i]=i; + + //testy xx(a); + myvec b = testy(a); + for (int i=0;i<10;i++) cout << b[i] << endl; + cout << "kernel end" << endl; +} + +PYTHON void getCurl(MACGrid& vel, Grid<Real>& vort, int comp) { + Grid<Vec3> velCenter(vel.getParent()), curl(vel.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GetComponent(curl, vort, comp); +} + +PYTHON void setinflow(FlagGrid& flags, MACGrid& vel, LevelsetGrid& phi, Real h) { + FOR_IJK(vel) { + if (i<=2) { + if (j < h*flags.getSizeY()) { + vel(i,j,k).x = 1; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 1; + phi(i,j,k) = -1; + } + } else { + vel(i,j,k).x = 0; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 4; + phi(i,j,k) = 1; + } + } + } + else if (i>=flags.getSizeX()-2) { + vel(i,j,k).x = 1; + /*if (j < 30-12) { + vel(i,j,k).x = 1; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 1; + phi(i,j,k) = -1; + } + } else { + vel(i,j,k).x = 0; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 4; + phi(i,j,k) = 1; + } + }*/ + } + } +} + +PYTHON void testDiscardNth (BasicParticleSystem& parts, int skip=1) { + //knSetPdataConst<Real>(pd,value); + for(int i=0; i<parts.size(); ++i) { + if(i%(skip+1) == skip) { // keep + } else { + parts.setPos(i, Vec3(-100000) ); + } + } +} + +} //namespace + diff --git a/source/blender/python/manta_full/source/turbulencepart.cpp b/source/blender/python/manta_full/source/turbulencepart.cpp new file mode 100644 index 00000000000..5a9bf7c7bfe --- /dev/null +++ b/source/blender/python/manta_full/source/turbulencepart.cpp @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.ynu.org/licenses + * + * Turbulence particles + * + ******************************************************************************/ + +#include "turbulencepart.h" +#include "shapes.h" +#include "randomstream.h" + +using namespace std; +namespace Manta { + +TurbulenceParticleSystem::TurbulenceParticleSystem(FluidSolver* parent, WaveletNoiseField& noise) : + ParticleSystem<TurbulenceParticleData>(parent), noise(noise) +{ +} + +ParticleBase* TurbulenceParticleSystem::clone() { + TurbulenceParticleSystem* nm = new TurbulenceParticleSystem(getParent(), noise); + compress(); + + nm->mData = mData; + nm->setName(getName()); + return nm; +} + +inline Vec3 hsv2rgb(Real h, Real s, Real v){ + Real r=0, g=0, b=0; + + int i = (int)(h * 6); + Real f = h * 6 - i; + Real p = v * (1 - s); + Real q = v * (1 - f * s); + Real t = v * (1 - (1 - f) * s); + + switch(i % 6){ + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + default: break; + } + + return Vec3(r,g,b); +} + +void TurbulenceParticleSystem::seed(Shape* shape, int num) { + static RandomStream rand(34894231); + Vec3 sz = shape->getExtent(), p0 = shape->getCenter() - sz*0.5; + for (int i=0; i<num; i++) { + Vec3 p; + do { + p = rand.getVec3() * sz + p0; + } while(!shape->isInside(p)); + Real z = (p.z - p0.z)/sz.z; + add(TurbulenceParticleData(p,hsv2rgb(z,0.75,1.0))); + } +} + +void TurbulenceParticleSystem::resetTexCoords(int num, const Vec3& inflow ) { + if (num==0) { + for (int i=0; i<size(); i++) mData[i].tex0 = mData[i].pos - inflow; + } else { + for (int i=0; i<size(); i++) mData[i].tex1 = mData[i].pos - inflow; + } +} + + +KERNEL(pts) +void KnSynthesizeTurbulence(TurbulenceParticleSystem& p, FlagGrid& flags, WaveletNoiseField& noise, Grid<Real>& kGrid, + Real alpha, Real dt, int octaves, Real scale, Real invL0, Real kmin) { + const Real PERSISTENCE = 0.56123f; + + const Vec3 pos(p[idx].pos); + if (flags.isInBounds(pos)) { // && !flags.isObstacle(pos)) { + Real k2 = kGrid.getInterpolated(pos)-kmin; + Real ks = k2<0 ? 0.0 : sqrt(k2); + + // Wavelet noise lookup + Real amplitude = scale * ks; + Real multiplier = invL0; + Vec3 vel(0.); + for (int o=0; o<octaves; o++) { + //Vec3 ns = noise.evaluateCurl(p[i].pos * multiplier) * amplitude; + Vec3 n0 = noise.evaluateCurl(p[idx].tex0 * multiplier) * amplitude; + Vec3 n1 = noise.evaluateCurl(p[idx].tex1 * multiplier) * amplitude; + vel += alpha * n0 + (1.0f-alpha) * n1; + + // next scale + amplitude *= PERSISTENCE; + multiplier *= 2.0f; + } + + // advection + Vec3 dx = vel*dt; + p[idx].pos += dx; + p[idx].tex0 += dx; + p[idx].tex1 += dx; + } +} + +void TurbulenceParticleSystem::synthesize(FlagGrid& flags, Grid<Real>& k, int octaves, Real switchLength, Real L0, Real scale, Vec3 inflowBias) { + static Real ctime = 0; + static Vec3 inflow(0.); + Real dt = getParent()->getDt(); + + // collect inflow bias + inflow += inflowBias * dt; + + // alpha: hat function over time + Real oldAlpha = 2.0f*nmod(ctime/switchLength, Real(1.0) ); + ctime += dt; + Real alpha = 2.0f*nmod(ctime/switchLength, Real(1.0) ); + + if (oldAlpha < 1.0f && alpha >= 1.0f) resetTexCoords(0, inflow); + if (oldAlpha > alpha) resetTexCoords(1, inflow); + if (alpha>1.0f) alpha=2.0f-alpha; + alpha = 1.0; + + KnSynthesizeTurbulence(*this, flags, noise, k, alpha, dt, octaves, scale, 1.0f/L0, 1.5*square(0.1)); +} + +void TurbulenceParticleSystem::deleteInObstacle(FlagGrid& flags) { + for (int i=0; i<size(); i++) + if (flags.isObstacle(mData[i].pos)) + mData[i].flag |= PDELETE; + compress(); +} + + +} // namespace diff --git a/source/blender/python/manta_full/source/turbulencepart.h b/source/blender/python/manta_full/source/turbulencepart.h new file mode 100644 index 00000000000..2e5542e97b1 --- /dev/null +++ b/source/blender/python/manta_full/source/turbulencepart.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Turbulence particles + * + ******************************************************************************/ + +#ifndef _TURBULENCEPART_H_ +#define _TURBULENCEPART_H_ + +#include "particle.h" +#include "noisefield.h" + +namespace Manta { +class Shape; + + + +struct TurbulenceParticleData { + TurbulenceParticleData() : pos(_0),color(1.),tex0(_0),tex1(_0),flag(0) {} + TurbulenceParticleData(const Vec3& p, const Vec3& color = Vec3(1.)) : pos(p),color(color),tex0(p),tex1(p),flag(0) {} + Vec3 pos, color; + Vec3 tex0, tex1; + int flag; + static ParticleBase::SystemType getType() { return ParticleBase::TURBULENCE; } +}; + +//! Turbulence particles +PYTHON class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> { +public: + PYTHON TurbulenceParticleSystem(FluidSolver* parent, WaveletNoiseField& noise); + + PYTHON void resetTexCoords(int num, const Vec3& inflow); + PYTHON void seed(Shape* source, int num); + PYTHON void synthesize(FlagGrid& flags, Grid<Real>& k, int octaves=2, Real switchLength=10.0, Real L0=0.1, Real scale=1.0, Vec3 inflowBias=_0); + PYTHON void deleteInObstacle(FlagGrid& flags); + + virtual ParticleBase* clone(); + +private: + WaveletNoiseField& noise; +}; + +} // namespace + + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/util/integrator.h b/source/blender/python/manta_full/source/util/integrator.h new file mode 100644 index 00000000000..378a890c0ab --- /dev/null +++ b/source/blender/python/manta_full/source/util/integrator.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for simple integration + * + ******************************************************************************/ + +#ifndef _INTEGRATE_H +#define _INTEGRATE_H + +#include <vector> +#include "vectorbase.h" +#include "kernel.h" + +namespace Manta { + +enum IntegrationMode { IntEuler=0, IntRK2, IntRK4 }; + +//! Integrate a particle set with a given velocity kernel +template<class VelKernel> +void integratePointSet(VelKernel& k, int mode) { + typedef typename VelKernel::type0 PosType; + PosType& x = k.getArg0(); + const std::vector<Vec3>& u = k.getRet(); + const int N = x.size(); + + if (mode == IntEuler) { + for(int i=0; i<N; i++) x[i].pos += u[i]; + } + else if (mode == IntRK2) { + PosType x0(x); + + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + 0.5*u[i]; + + k.run(); + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + u[i]; + } + else if (mode == IntRK4) { + PosType x0(x); + std::vector<Vec3> uTotal(u); + + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + 0.5*u[i]; + + k.run(); + for(int i=0; i<N; i++) { + x[i].pos = x0[i].pos + 0.5*u[i]; + uTotal[i] += 2*u[i]; + } + + k.run(); + for(int i=0; i<N; i++) { + x[i].pos = x0[i].pos + u[i]; + uTotal[i] += 2*u[i]; + } + + k.run(); + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + (_1/6) * (uTotal[i] + u[i]); + } + else + errMsg("unknown integration type"); + + //for(int i=0; i<N; i++) std::cout << x[i].pos.y-x[0].pos.y << std::endl; + //std::cout << "<><><>" << std::endl; +} + + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/util/interpol.h b/source/blender/python/manta_full/source/util/interpol.h new file mode 100644 index 00000000000..073df798ffa --- /dev/null +++ b/source/blender/python/manta_full/source/util/interpol.h @@ -0,0 +1,209 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for interpolation + * + ******************************************************************************/ + +#ifndef _INTERPOL_H +#define _INTERPOL_H + +#include "vectorbase.h" + +// Grid values are stored at i+0.5, j+0.5, k+0.5 +// MAC grid values are stored at i,j+0.5,k+0.5 (for x) ... + +namespace Manta { + +inline Vec3 fdTangent(const Vec3& p0, const Vec3& p1, const Vec3& p2) { + return 0.5*(getNormalized(p2-p1) + getNormalized(p1-p0)); +} + +inline Vec3 crTangent(const Vec3& p0, const Vec3& p1, const Vec3& p2) { + return 0.5*(p2-p0); +} + +inline Vec3 hermiteSpline(const Vec3& p0, const Vec3& p1, const Vec3& m0, const Vec3& m1, Real t) { + const Real t2=t*t, t3=t2*t; + return (2.0*t3 - 3.0*t2 + 1.0)*p0 + (t3 - 2.0*t2 + t)*m0 + (-2.0*t3 + 3.0*t2)*p1 + (t3 - t2)*m1; +} + +static inline void checkIndexInterpol(const Vec3i& size, int idx) { + if (idx<0 || idx > size.x * size.y * size.z) { + std::ostringstream s; + s << "Grid interpol dim " << size << " : index " << idx << " out of bound "; + errMsg(s.str()); + } +} + + +// ---------------------------------------------------------------------- +// Grid interpolators +// ---------------------------------------------------------------------- + +#define BUILD_INDEX \ + Real px=pos.x-0.5f, py=pos.y-0.5f, pz=pos.z-0.5f; \ + int xi = (int)px; \ + int yi = (int)py; \ + int zi = (int)pz; \ + Real s1 = px-(Real)xi, s0 = 1.-s1; \ + Real t1 = py-(Real)yi, t0 = 1.-t1; \ + Real f1 = pz-(Real)zi, f0 = 1.-f1; \ + /* clamp to border */ \ + if (px < 0.) { xi = 0; s0 = 1.0; s1 = 0.0; } \ + if (py < 0.) { yi = 0; t0 = 1.0; t1 = 0.0; } \ + if (pz < 0.) { zi = 0; f0 = 1.0; f1 = 0.0; } \ + if (xi >= size.x-1) { xi = size.x-2; s0 = 0.0; s1 = 1.0; } \ + if (yi >= size.y-1) { yi = size.y-2; t0 = 0.0; t1 = 1.0; } \ + if (size.z>1) { if (zi >= size.z-1) { zi = size.z-2; f0 = 0.0; f1 = 1.0; } } \ + const int X = 1; \ + const int Y = size.x; + +template <class T> +inline T interpol(const T* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx] *t0 + data[idx+Y] *t1) * s0 + + (data[idx+X]*t0 + data[idx+X+Y]*t1) * s1) * f0 + +((data[idx+Z]*t0 + data[idx+Y+Z]*t1) * s0 + + (data[idx+X+Z]*t0 + data[idx+X+Y+Z]*t1) * s1) * f1; +} + +template <int c> +inline Real interpolComponent(const Vec3* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx][c] *t0 + data[idx+Y][c] *t1) * s0 + + (data[idx+X][c]*t0 + data[idx+X+Y][c]*t1) * s1) * f0 + +((data[idx+Z][c]*t0 + data[idx+Y+Z][c]*t1) * s0 + + (data[idx+X+Z][c]*t0 + data[idx+X+Y+Z][c]*t1) * s1) * f1; +} + +template<class T> +inline void setInterpol(T* data, const Vec3i& size, const int Z, const Vec3& pos, const T& v, Real* sumBuffer) +{ + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + T* ref = &data[idx]; + Real* sum = &sumBuffer[idx]; + Real s0f0=s0*f0, s1f0=s1*f0, s0f1=s0*f1, s1f1=s1*f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[Z] += wz; sum[X+Z] += wxz; sum[Y+Z] += wyz; sum[X+Y+Z] += wxyz; + ref[Z] += wz*v; ref[X+Z] += wxz*v; ref[Y+Z] += wyz*v; ref[X+Y+Z] += wxyz*v; + sum[0] += w0; sum[X] += wx; sum[Y] += wy; sum[X+Y] += wxy; + ref[0] += w0*v; ref[X] += wx*v; ref[Y] += wy*v; ref[X+Y] += wxy*v; +} + + +#define BUILD_INDEX_SHIFT \ + BUILD_INDEX \ + /* shifted coords */ \ + int s_xi = (int)pos.x, s_yi = (int)pos.y, s_zi = (int)pos.z; \ + Real s_s1 = pos.x-(Real)s_xi, s_s0 = 1.-s_s1; \ + Real s_t1 = pos.y-(Real)s_yi, s_t0 = 1.-s_t1; \ + Real s_f1 = pos.z-(Real)s_zi, s_f0 = 1.-s_f1; \ + /* clamp to border */ \ + if (pos.x < 0) { s_xi = 0; s_s0 = 1.0; s_s1 = 0.0; } \ + if (pos.y < 0) { s_yi = 0; s_t0 = 1.0; s_t1 = 0.0; } \ + if (pos.z < 0) { s_zi = 0; s_f0 = 1.0; s_f1 = 0.0; } \ + if (s_xi >= size.x-1) { s_xi = size.x-2; s_s0 = 0.0; s_s1 = 1.0; } \ + if (s_yi >= size.y-1) { s_yi = size.y-2; s_t0 = 0.0; s_t1 = 1.0; } \ + if (size.z>1) { if (s_zi >= size.z-1) { s_zi = size.z-2; s_f0 = 0.0; s_f1 = 1.0; } } + +inline Vec3 interpolMAC(const Vec3* data, const Vec3i& size, const int Z, const Vec3& pos) +{ + BUILD_INDEX_SHIFT + DEBUG_ONLY(checkIndexInterpol(size,(zi*size.y+yi)*size.x+xi)); + DEBUG_ONLY(checkIndexInterpol(size,(s_zi*size.y+s_yi)*size.x+s_xi+X+Y+Z)); + + // process individual components + Vec3 ret(0.); + { // X + const Vec3* ref = &data[((zi*size.y+yi)*size.x+s_xi)]; + ret.x = f0 * ((ref[0].x*t0 + ref[Y].x*t1 )*s_s0 + + (ref[X].x*t0 + ref[X+Y].x*t1 )*s_s1) + + f1 * ((ref[Z].x*t0 + ref[Z+Y].x*t1 )*s_s0 + + (ref[X+Z].x*t0 + ref[X+Y+Z].x*t1 )*s_s1 ); + } + { // Y + const Vec3* ref = &data[((zi*size.y+s_yi)*size.x+xi)]; + ret.y = f0 * ((ref[0].y*s_t0 + ref[Y].y*s_t1 )*s0 + + (ref[X].y*s_t0 + ref[X+Y].y*s_t1 )*s1) + + f1 * ((ref[Z].y*s_t0 + ref[Z+Y].y*s_t1 )*s0 + + (ref[X+Z].y*s_t0 + ref[X+Y+Z].y*s_t1 )*s1 ); + } + { // Z + const Vec3* ref = &data[((s_zi*size.y+yi)*size.x+xi)]; + ret.z = s_f0 * ((ref[0].z*t0 + ref[Y].z*t1 )*s0 + + (ref[X].z*t0 + ref[X+Y].z*t1 )*s1) + + s_f1 * ((ref[Z].z*t0 + ref[Z+Y].z*t1 )*s0 + + (ref[X+Z].z*t0 + ref[X+Y+Z].z*t1 )*s1 ); + } + return ret; +} + +inline void setInterpolMAC(Vec3* data, const Vec3i& size, const int Z, const Vec3& pos, const Vec3& val, Vec3* sumBuffer) +{ + BUILD_INDEX_SHIFT + DEBUG_ONLY(checkIndexInterpol(size,(zi*size.y+yi)*size.x+xi)); + DEBUG_ONLY(checkIndexInterpol(size,(s_zi*size.y+s_yi)*size.x+s_xi+X+Y+Z)); + + // process individual components + { // X + const int idx = (zi*size.y+yi)*size.x+s_xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s_s0*f0, s1f0=s_s1*f0, s0f1=s_s0*f1, s1f1=s_s1*f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[Z].x += wz; sum[X+Z].x += wxz; sum[Y+Z].x += wyz; sum[X+Y+Z].x += wxyz; + ref[Z].x += wz*val.x; ref[X+Z].x += wxz*val.x; ref[Y+Z].x += wyz*val.x; ref[X+Y+Z].x += wxyz*val.x; + sum[0].x += w0; sum[X].x += wx; sum[Y].x += wy; sum[X+Y].x += wxy; + ref[0].x += w0*val.x; ref[X].x += wx*val.x; ref[Y].x += wy*val.x; ref[X+Y].x += wxy*val.x; + } + { // Y + const int idx = (zi*size.y+s_yi)*size.x+xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s0*f0, s1f0=s1*f0, s0f1=s0*f1, s1f1=s1*f1; + Real w0 = s_t0*s0f0, wx = s_t0*s1f0, wy = s_t1*s0f0, wxy = s_t1*s1f0; + Real wz = s_t0*s0f1, wxz = s_t0*s1f1, wyz = s_t1*s0f1, wxyz = s_t1*s1f1; + + sum[Z].y += wz; sum[X+Z].y += wxz; sum[Y+Z].y += wyz; sum[X+Y+Z].y += wxyz; + ref[Z].y += wz*val.y; ref[X+Z].y += wxz*val.y; ref[Y+Z].y += wyz*val.y; ref[X+Y+Z].y += wxyz*val.y; + sum[0].y += w0; sum[X].y += wx; sum[Y].y += wy; sum[X+Y].y += wxy; + ref[0].y += w0*val.y; ref[X].y += wx*val.y; ref[Y].y += wy*val.y; ref[X+Y].y += wxy*val.y; + } + { // Z + const int idx = (s_zi*size.y+yi)*size.x+xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s0*s_f0, s1f0=s1*s_f0, s0f1=s0*s_f1, s1f1=s1*s_f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[0].z += w0; sum[X].z += wx; sum[Y].z += wy; sum[X+Y].z += wxy; + sum[Z].z += wz; sum[X+Z].z += wxz; sum[Y+Z].z += wyz; sum[X+Y+Z].z += wxyz; + ref[0].z += w0*val.z; ref[X].z += wx*val.z; ref[Y].z += wy*val.z; ref[X+Y].z += wxy*val.z; + ref[Z].z += wz*val.z; ref[X+Z].z += wxz*val.z; ref[Y+Z].z += wyz*val.z; ref[X+Y+Z].z += wxyz*val.z; + } +} + +#undef BUILD_INDEX +#undef BUILD_INDEX_SHIFT + +} //namespace + +#endif diff --git a/source/blender/python/manta_full/source/util/interpolHigh.h b/source/blender/python/manta_full/source/util/interpolHigh.h new file mode 100644 index 00000000000..6778e5f7d27 --- /dev/null +++ b/source/blender/python/manta_full/source/util/interpolHigh.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for higher order interpolation + * + ******************************************************************************/ + +#ifndef _INTERPOLHIGH_H +#define _INTERPOLHIGH_H + +#include "vectorbase.h" + +namespace Manta { + +// copied from interpol.h +#define BUILD_INDEX \ + Real px=pos.x-0.5f, py=pos.y-0.5f, pz=pos.z-0.5f; \ + int xi = (int)px; \ + int yi = (int)py; \ + int zi = (int)pz; \ + Real s1 = px-(Real)xi, s0 = 1.-s1; \ + Real t1 = py-(Real)yi, t0 = 1.-t1; \ + Real f1 = pz-(Real)zi, f0 = 1.-f1; \ + /* clamp to border */ \ + if (px < 0.) { xi = 0; s0 = 1.0; s1 = 0.0; } \ + if (py < 0.) { yi = 0; t0 = 1.0; t1 = 0.0; } \ + if (pz < 0.) { zi = 0; f0 = 1.0; f1 = 0.0; } \ + if (xi >= size.x-1) { xi = size.x-2; s0 = 0.0; s1 = 1.0; } \ + if (yi >= size.y-1) { yi = size.y-2; t0 = 0.0; t1 = 1.0; } \ + if (size.z>1) { if (zi >= size.z-1) { zi = size.z-2; f0 = 0.0; f1 = 1.0; } } \ + const int X = 1; \ + const int Y = size.x; + +template <class T> +inline T interpolCubic(const T* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx] *t0 + data[idx+Y] *t1) * s0 + + (data[idx+X]*t0 + data[idx+X+Y]*t1) * s1) * f0 + +((data[idx+Z]*t0 + data[idx+Y+Z]*t1) * s0 + + (data[idx+X+Z]*t0 + data[idx+X+Y+Z]*t1) * s1) * f1; +} + +#undef BUILD_INDEX + +} //namespace + +#endif + + diff --git a/source/blender/python/manta_full/source/util/mcubes.h b/source/blender/python/manta_full/source/util/mcubes.h new file mode 100644 index 00000000000..39bd5970791 --- /dev/null +++ b/source/blender/python/manta_full/source/util/mcubes.h @@ -0,0 +1,323 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Marching cubes lookup indices + * + ******************************************************************************/ + +#ifndef _MCUBES_H_ +#define _MCUBES_H_ + +static const int mcEdges[24] = { + 0,1, 1,2, 2,3, 3,0, + 4,5, 5,6, 6,7, 7,4, + 0,4, 1,5, 2,6, 3,7 }; + +static const int cubieOffsetX[8] = { 0,1,1,0, 0,1,1,0 }; +static const int cubieOffsetY[8] = { 0,0,1,1, 0,0,1,1 }; +static const int cubieOffsetZ[8] = { 0,0,0,0, 1,1,1,1 }; + +/* which edges are needed ? */ +/* cf. http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ */ +static const short mcEdgeTable[256]={ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; + +/* triangles for the 256 intersection possibilities */ +/* cf. http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ */ +static const short mcTriTable[256][16] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/util/quaternion.h b/source/blender/python/manta_full/source/util/quaternion.h new file mode 100644 index 00000000000..1808bb582a6 --- /dev/null +++ b/source/blender/python/manta_full/source/util/quaternion.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic quaternion class + * + ******************************************************************************/ + +#ifndef _QUATERNION_H +#define _QUATERNION_H + +#include "vectorbase.h" + +namespace Manta { + +//! Very basic quaternion class +class Quaternion { +public: + + //! default constructor + Quaternion() : x(0), y(0), z(0), w(0) {} + + //! copy constructor + Quaternion(const Quaternion& q) : x(q.x), y(q.y), z(q.z), w(q.w) {} + + //! construct a quaternion from members + Quaternion(Real _x, Real _y, Real _z, Real _w) : x(_x), y(_y), z(_z), w(_w) {} + + //! construct a quaternion from imag/real parts + Quaternion(Vec3 i, Real r) : x(i.x), y(i.y), z(i.z), w(r) {} + + //! Assign operator + inline Quaternion& operator= (const Quaternion& q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + return *this; + } + + //! Assign multiplication operator + inline Quaternion& operator*= ( const Real a ) { + x *= a; + y *= a; + z *= a; + w *= a; + return *this; + } + + //! return inverse quaternion + inline Quaternion inverse() const { + Real mag = 1.0/(x*x+y*y+z*z+w*w); + return Quaternion(-x*mag,-y*mag,-z*mag,w*mag); + } + + //! imaginary part accessor + inline Vec3 imag() { return Vec3(x,y,z); } + + // imaginary part + Real x; + Real y; + Real z; + + // real part + Real w; +}; + + +//! Multiplication operator +inline Quaternion operator* ( const Quaternion &q1, const Quaternion &q2 ) { + return Quaternion ( q2.w * q1.x + q2.x * q1.w + q2.y * q1.z - q2.z * q1.y, + q2.w * q1.y + q2.y * q1.w + q2.z * q1.x - q2.x * q1.z, + q2.w * q1.z + q2.z * q1.w + q2.x * q1.y - q2.y * q1.x, + q2.w * q1.w - q2.x * q1.x - q2.y * q1.y - q2.z * q1.z ); +} + +//! Multiplication operator +inline Quaternion operator* ( const Quaternion &q, const Real a ) { + return Quaternion ( q.x*a, q.y*a, q.z*a, q.w*a); +} + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/util/randomstream.h b/source/blender/python/manta_full/source/util/randomstream.h new file mode 100644 index 00000000000..23380a4c10f --- /dev/null +++ b/source/blender/python/manta_full/source/util/randomstream.h @@ -0,0 +1,377 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Random numbers + * + * Based on GPL code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus + * Richard J. Wagner v0.5 7 November 2000 rjwagner@writeme.com + * + ******************************************************************************/ + +#ifndef _RANDOMSTREAM_H +#define _RANDOMSTREAM_H + +namespace Manta { + +#include <iostream> +#include <limits.h> +#include <stdio.h> +#include <time.h> +#include <math.h> +#include "vectorbase.h" + +class MTRand { + // Data + public: + typedef unsigned long uint32; // unsigned integer type, at least 32 bits + + enum { N = 624 }; // length of state vector + enum { SAVE = N + 1 }; // length of array for save() + + protected: + enum { M = 397 }; // period parameter + + uint32 state[N]; // internal state + uint32 *pNext; // next value to get from state + int left; // number of values left before reload needed + + + //Methods + public: + MTRand( const uint32& oneSeed ); // initialize with a simple uint32 + MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array + MTRand(); // auto-initialize with /dev/urandom or time() and clock() + + // Do NOT use for CRYPTOGRAPHY without securely hashing several returned + // values together, otherwise the generator state can be learned after + // reading 624 consecutive values. + + // Access to 32-bit random numbers + double rand(); // real number in [0,1] + double rand( const double& n ); // real number in [0,n] + double randExc(); // real number in [0,1) + double randExc( const double& n ); // real number in [0,n) + double randDblExc(); // real number in (0,1) + double randDblExc( const double& n ); // real number in (0,n) + uint32 randInt(); // integer in [0,2^32-1] + uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 + double operator()() { return rand(); } // same as rand() + + // Access to 53-bit random numbers (capacity of IEEE double precision) + double rand53(); // real number in [0,1) + + // Access to nonuniform random number distributions + double randNorm( const double& mean = 0.0, const double& variance = 1.0 ); + + // Re-seeding functions with same behavior as initializers + void seed( const uint32 oneSeed ); + void seed( uint32 *const bigSeed, const uint32 seedLength = N ); + void seed(); + + // Saving and loading generator state + void save( uint32* saveArray ) const; // to array of size SAVE + void load( uint32 *const loadArray ); // from such array + friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); + friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); + + protected: + void initialize( const uint32 oneSeed ); + void reload(); + uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } + uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } + uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } + uint32 mixBits( const uint32& u, const uint32& v ) const { + return hiBit(u) | loBits(v); + } + uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const { + return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); + } + static uint32 hash( time_t t, clock_t c ); +}; + + +inline MTRand::MTRand( const uint32& oneSeed ) + { seed(oneSeed); } + +inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) + { seed(bigSeed,seedLength); } + +inline MTRand::MTRand() + { seed(); } + +inline double MTRand::rand() + { return double(randInt()) * (1.0/4294967295.0); } + +inline double MTRand::rand( const double& n ) + { return rand() * n; } + +inline double MTRand::randExc() + { return double(randInt()) * (1.0/4294967296.0); } + +inline double MTRand::randExc( const double& n ) + { return randExc() * n; } + +inline double MTRand::randDblExc() + { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } + +inline double MTRand::randDblExc( const double& n ) + { return randDblExc() * n; } + +inline double MTRand::rand53() +{ + uint32 a = randInt() >> 5, b = randInt() >> 6; + return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada +} + +inline double MTRand::randNorm( const double& mean, const double& variance ) +{ + // Return a real number from a normal (Gaussian) distribution with given + // mean and variance by Box-Muller method + double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; + double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); + return mean + r * cos(phi); +} + +inline MTRand::uint32 MTRand::randInt() +{ + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + + if( left == 0 ) reload(); + --left; + + register uint32 s1; + s1 = *pNext++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680UL; + s1 ^= (s1 << 15) & 0xefc60000UL; + return ( s1 ^ (s1 >> 18) ); +} + +inline MTRand::uint32 MTRand::randInt( const uint32& n ) +{ + // Find which bits are used in n + // Optimized by Magnus Jonsson (magnus@smartelectronix.com) + uint32 used = n; + used |= used >> 1; + used |= used >> 2; + used |= used >> 4; + used |= used >> 8; + used |= used >> 16; + + // Draw numbers until one is found in [0,n] + uint32 i; + do + i = randInt() & used; // toss unused bits to shorten search + while( i > n ); + return i; +} + + +inline void MTRand::seed( const uint32 oneSeed ) +{ + // Seed the generator with a simple uint32 + initialize(oneSeed); + reload(); +} + + +inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) +{ + // Seed the generator with an array of uint32's + // There are 2^19937-1 possible initial states. This function allows + // all of those to be accessed by providing at least 19937 bits (with a + // default seed length of N = 624 uint32's). Any bits above the lower 32 + // in each element are discarded. + // Just call seed() if you want to get array from /dev/urandom + initialize(19650218UL); + const unsigned int Nenum = N; + register int i = 1; + register uint32 j = 0; + register int k = ( Nenum > seedLength ? Nenum : seedLength ); + for( ; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); + state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; + state[i] &= 0xffffffffUL; + ++i; ++j; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + if( j >= seedLength ) j = 0; + } + for( k = N - 1; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); + state[i] -= i; + state[i] &= 0xffffffffUL; + ++i; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + } + state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array + reload(); +} + + +inline void MTRand::seed() +{ + // Seed the generator with an array from /dev/urandom if available + // Otherwise use a hash of time() and clock() values + + // First try getting an array from /dev/urandom + FILE* urandom = fopen( "/dev/urandom", "rb" ); + if( urandom ) + { + uint32 bigSeed[N]; + register uint32 *s = bigSeed; + register int i = N; + register bool success = true; + while( success && i-- ) + success = fread( s++, sizeof(uint32), 1, urandom ); + fclose(urandom); + if( success ) { seed( bigSeed, N ); return; } + } + + // Was not successful, so use time() and clock() instead + seed( hash( time(NULL), clock() ) ); +} + + +inline void MTRand::initialize( const uint32 intseed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register uint32 *s = state; + register uint32 *r = state; + register int i = 1; + *s++ = intseed & 0xffffffffUL; + for( ; i < N; ++i ) + { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; + r++; + } +} + + +inline void MTRand::reload() +{ + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + register uint32 *p = state; + register int i; + for( i = N - M; i--; ++p ) + *p = twist( p[M], p[0], p[1] ); + for( i = M; --i; ++p ) + *p = twist( p[M-N], p[0], p[1] ); + *p = twist( p[M-N], p[0], state[0] ); + + left = N, pNext = state; +} + + +inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= UCHAR_MAX + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= UCHAR_MAX + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + + +inline void MTRand::save( uint32* saveArray ) const +{ + register uint32 *sa = saveArray; + register const uint32 *s = state; + register int i = N; + for( ; i--; *sa++ = *s++ ) {} + *sa = left; +} + + +inline void MTRand::load( uint32 *const loadArray ) +{ + register uint32 *s = state; + register uint32 *la = loadArray; + register int i = N; + for( ; i--; *s++ = *la++ ) {} + left = *la; + pNext = &state[N-left]; +} + + +inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) +{ + register const MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; os << *s++ << "\t" ) {} + return os << mtrand.left; +} + + +inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) +{ + register MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; is >> *s++ ) {} + is >> mtrand.left; + mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; + return is; +} + +// simple interface to mersenne twister +class RandomStream +{ +public: + inline RandomStream(long seed) : mtr(seed) {} ; + ~RandomStream() {} + + /*! get a random number from the stream */ + inline double getDouble( void ) { return mtr.rand(); }; + inline float getFloat ( void ) { return (float)mtr.rand(); }; + + inline float getFloat( float min, float max ) { return mtr.rand(max-min) + min; }; + inline float getRandNorm( float mean, float var) { return mtr.randNorm(mean, var); }; + + #if FLOATINGPOINT_PRECISION==1 + inline Real getReal() { return getFloat(); } + + #else + inline Real getReal() { return getDouble(); } + #endif + + inline Vec3 getVec3 () { Real a=getReal(), b=getReal(), c=getReal(); return Vec3(a,b,c); } + inline Vec3 getVec3Norm () { Vec3 a=getVec3(); normalize(a); return a; } + +private: + MTRand mtr; +}; + + +} // namespace + +#endif diff --git a/source/blender/python/manta_full/source/util/solvana.h b/source/blender/python/manta_full/source/util/solvana.h new file mode 100644 index 00000000000..c9c8167bceb --- /dev/null +++ b/source/blender/python/manta_full/source/util/solvana.h @@ -0,0 +1,176 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Analytical solutions to some problems + * generated using MATLAB symbolic math ccode + * + ******************************************************************************/ + +#ifndef _SOLVANA_H +#define _SOLVANA_H + +//! solves the equation [e1 e2 e3; 1 1 1]*x = g using least squares +inline void SolveOverconstraint34(float e1x, float e1y, float e1z, + float e2x, float e2y, float e2z, + float e3x, float e3y, float e3z, + float g1, float g2, float g3, + float& x1, float& x2, float& x3) +{ + float e1x2 = e1x*e1x, e1y2 = e1y*e1y, e1z2 = e1z*e1z; + float e2x2 = e2x*e2x, e2y2 = e2y*e2y, e2z2 = e2z*e2z; + float e3x2 = e3x*e3x, e3y2 = e3y*e3y, e3z2 = e3z*e3z; + float e1xy = e1x*e1y, e1xz = e1x*e1z, e1yz = e1y*e1z; + float e2xy = e2x*e2y, e2xz = e2x*e2z, e2yz = e2y*e2z; + float e3xy = e3x*e3y, e3xz = e3x*e3z, e3yz = e3y*e3z; + float e12x = e1x*e2x, e12y = e1y*e2y, e12z = e1z*e2z; + float e13x = e1x*e3x, e13y = e1y*e3y, e13z = e1z*e3z; + float e23x = e2x*e3x, e23y = e2y*e3y, e23z = e2z*e3z; + float t1543 = e3y2*e2x2; + float t1544 = e3x2*e2y2; + float t1545 = e3z2*e2x2; + float t1546 = e3x2*e2z2; + float t1547 = e3z2*e2y2; + float t1548 = e3y2*e2z2; + float t1549 = e2y2*e1x2; + float t1550 = e2x2*e1y2; + float t1551 = e2z2*e1x2; + float t1552 = e2x2*e1z2; + float t1553 = e2z2*e1y2; + float t1554 = e2y2*e1z2; + float t1555 = e3y2*e1x2; + float t1556 = e3x2*e1y2; + float t1557 = e3z2*e1x2; + float t1558 = e3x2*e1z2; + float t1559 = e3z2*e1y2; + float t1560 = e3y2*e1z2; + float t1561 = e3z2*e2y2*e1x2; + float t1562 = e3y2*e2z2*e1x2; + float t1563 = e3z2*e2x2*e1y2; + float t1564 = e3x2*e2z2*e1y2; + float t1565 = e3y2*e2x2*e1z2; + float t1566 = e3x2*e2y2*e1z2; + float t1567 = e1xy*e2x*e3y*2.0; + float t1568 = e1xy*e2y*e3x*2.0; + float t1569 = e1xz*e2x*e3z*2.0; + float t1570 = e1xz*e2z*e3x*2.0; + float t1571 = e1yz*e2y*e3z*2.0; + float t1572 = e1yz*e2z*e3y*2.0; + float t1573 = e1x*e2xy*e3y*2.0; + float t1574 = e1y*e2xy*e3x*2.0; + float t1575 = e1x*e2xz*e3z*2.0; + float t1576 = e1z*e2xz*e3x*2.0; + float t1577 = e1y*e2yz*e3z*2.0; + float t1578 = e1z*e2yz*e3y*2.0; + float t1579 = e1x*e2y*e3xy*2.0; + float t1580 = e1y*e2x*e3xy*2.0; + float t1581 = e1x*e2z*e3xz*2.0; + float t1582 = e1z*e2x*e3xz*2.0; + float t1583 = e1y*e2z*e3yz*2.0; + float t1584 = e1z*e2y*e3yz*2.0; + float t1585 = e1xy*e2xz*e3yz*2.0; + float t1586 = e1xy*e2yz*e3xz*2.0; + float t1587 = e1xz*e2xy*e3yz*2.0; + float t1588 = e1xz*e2yz*e3xy*2.0; + float t1589 = e1yz*e2xy*e3xz*2.0; + float t1590 = e1yz*e2xz*e3xy*2.0; + float t1596 = e12x*e3y2*2.0; + float t1597 = e13x*e2y2*2.0; + float t1598 = e23x*e1y2*2.0; + float t1599 = e12x*e3z2*2.0; + float t1600 = e13x*e2z2*2.0; + float t1601 = e12y*e3x2*2.0; + float t1602 = e13y*e2x2*2.0; + float t1603 = e23y*e1x2*2.0; + float t1604 = e23x*e1z2*2.0; + float t1605 = e12y*e3z2*2.0; + float t1606 = e13y*e2z2*2.0; + float t1607 = e12z*e3x2*2.0; + float t1608 = e13z*e2x2*2.0; + float t1609 = e23z*e1x2*2.0; + float t1610 = e23y*e1z2*2.0; + float t1611 = e12z*e3y2*2.0; + float t1612 = e13z*e2y2*2.0; + float t1613 = e23z*e1y2*2.0; + float t1614 = e1xy*e2xy*2.0; + float t1615 = e1xz*e2xz*2.0; + float t1616 = e1yz*e2yz*2.0; + float t1617 = e1xy*e3xy*2.0; + float t1618 = e1xz*e3xz*2.0; + float t1619 = e1yz*e3yz*2.0; + float t1620 = e2xy*e3xy*2.0; + float t1621 = e2xz*e3xz*2.0; + float t1622 = e2yz*e3yz*2.0; + float t1623 = e1xy*e2xy*e3z2*2.0; + float t1624 = e1xz*e2xz*e3y2*2.0; + float t1625 = e1yz*e2yz*e3x2*2.0; + float t1626 = e1xy*e3xy*e2z2*2.0; + float t1627 = e1xz*e3xz*e2y2*2.0; + float t1628 = e1yz*e3yz*e2x2*2.0; + float t1629 = e2xy*e3xy*e1z2*2.0; + float t1630 = e2xz*e3xz*e1y2*2.0; + float t1631 = e2yz*e3yz*e1x2*2.0; + float t1591 = t1550+t1551+t1560+t1543+t1552+t1561+t1570+t1544+t1553+t1562+t1571+t1580+t1545+t1554+t1563+t1572+t1581+t1590+t1546+t1555+t1564+t1573+t1582+t1547+t1556+t1565+t1574+t1583+t1548+t1557+t1566+t1575+t1584+t1549+t1558+t1567+t1576+t1585+t1559+t1568+t1577+t1586+t1569+t1578+t1587-t1596+t1579+t1588-t1597+t1589-t1598-t1599-t1600-t1601-t1610-t1602-t1611-t1620-t1603-t1612-t1621-t1630-t1604-t1613-t1622-t1631-t1605-t1614-t1623-t1606-t1615-t1624-t1607-t1616-t1625-t1608-t1617-t1626-t1609-t1618-t1627-t1619-t1628-t1629; + float t1592 = 1.0/t1591; + float t1635 = e13x*e2y2; + float t1636 = e13x*e2z2; + float t1637 = e13y*e2x2; + float t1638 = e13y*e2z2; + float t1639 = e13z*e2x2; + float t1640 = e13z*e2y2; + float t1653 = e23x*2.0; + float t1654 = e23y*2.0; + float t1655 = e23z*2.0; + float t1641 = e3x2+e3z2+e3y2+e2y2+t1543+e2z2+t1544+e2x2+t1545+t1546+t1547+t1548-t1620-t1621-t1622-t1653-t1654-t1655; + float t1642 = e12x*e3y2; + float t1643 = e12x*e3z2; + float t1644 = e12y*e3x2; + float t1645 = e12y*e3z2; + float t1646 = e12z*e3x2; + float t1647 = e12z*e3y2; + float t1656 = e1x*e2y*e3xy; + float t1657 = e1y*e2x*e3xy; + float t1658 = e1x*e2z*e3xz; + float t1659 = e1z*e2x*e3xz; + float t1660 = e1y*e2z*e3yz; + float t1661 = e1z*e2y*e3yz; + float t1648 = e3x2+e3z2+e3y2-e13x-e13y-e13z+e12x-e23y+e12y+t1642-e23z-t1660+e12z+t1643-t1661+t1644+t1645+t1646+t1647-t1656-t1657-e23x-t1658-t1659; + float t1679 = e1x*e2xy*e3y; + float t1680 = e1y*e2xy*e3x; + float t1681 = e1x*e2xz*e3z; + float t1682 = e1z*e2xz*e3x; + float t1683 = e1y*e2yz*e3z; + float t1684 = e1z*e2yz*e3y; + float t1652 = e2y2+e2z2+e2x2+e13x+e13y+e13z+t1640-e12x-e23y-e12y-e23z-e12z+t1635-t1680+t1636-t1681+t1637-t1682+t1638-t1683+t1639-t1684-e23x-t1679; + float t1662 = e23x*e1y2; + float t1663 = e23y*e1x2; + float t1664 = e23x*e1z2; + float t1665 = e23z*e1x2; + float t1666 = e23y*e1z2; + float t1667 = e23z*e1y2; + float t1670 = e1xy*e2x*e3y; + float t1671 = e1xy*e2y*e3x; + float t1672 = e1xz*e2x*e3z; + float t1673 = e1xz*e2z*e3x; + float t1674 = e1yz*e2y*e3z; + float t1675 = e1yz*e2z*e3y; + float t1668 = e1x2+e1y2+e1z2-e13x-e13y-e13z-e12x+e23y-e12y+e23z-e12z-t1670+t1662-t1671+t1663-t1672+t1664-t1673+t1665-t1674+t1666-t1675+e23x+t1667; + float t1676 = e13x*2.0; + float t1677 = e13y*2.0; + float t1678 = e13z*2.0; + float t1669 = e3x2+e3z2+e3y2+t1560+e1x2+t1555+e1y2+t1556+e1z2+t1557+t1558+t1559-t1617-t1618-t1619-t1676-t1677-t1678; + float t1686 = e12x*2.0; + float t1687 = e12y*2.0; + float t1688 = e12z*2.0; + float t1685 = t1550+t1551+e2y2+t1552+e2z2+t1553+e2x2+t1554+e1x2+e1y2+e1z2+t1549-t1614-t1615-t1616-t1686-t1687-t1688; + x1 =-g2*(-e1y*t1592*t1641+e2y*t1592*t1648+e3y*t1592*t1652)-g3*(-e1z*t1592*t1641+e2z*t1592*t1648+e3z*t1592*t1652)-g1*(-e1x*t1592*t1641+e2x*t1592*t1648+e3x*t1592*(e2y2+e2z2+e2x2+e13x+e13y+e13z+t1640+t1635+t1636+t1637+t1638+t1639-e12x-e12y-e12z-e23x-e23y-e23z-e1x*e2xy*e3y-e1y*e2xy*e3x-e1x*e2xz*e3z-e1z*e2xz*e3x-e1y*e2yz*e3z-e1z*e2yz*e3y)); + x2 =-g1*(e1x*t1592*t1648-e2x*t1592*t1669+e3x*t1592*t1668)-g2*(e1y*t1592*t1648-e2y*t1592*t1669+e3y*t1592*t1668)-g3*(e1z*t1592*t1648-e2z*t1592*t1669+e3z*t1592*t1668); + x3 =-g1*(e1x*t1592*t1652+e2x*t1592*t1668-e3x*t1592*t1685)-g2*(e1y*t1592*t1652+e2y*t1592*t1668-e3y*t1592*t1685)-g3*(e1z*t1592*t1652+e2z*t1592*t1668-e3z*t1592*t1685); +} + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/util/vectorbase.cpp b/source/blender/python/manta_full/source/util/vectorbase.cpp new file mode 100644 index 00000000000..5f7dee46f59 --- /dev/null +++ b/source/blender/python/manta_full/source/util/vectorbase.cpp @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic vector class + * + ******************************************************************************/ + +#include "vectorbase.h" +#include <limits> +#include <math.h> + +using namespace std; + +namespace Manta { + +template<> const Vector3D<int> Vector3D<int>::Zero( 0, 0, 0 ); +template<> const Vector3D<float> Vector3D<float>::Zero( 0.f, 0.f, 0.f ); +template<> const Vector3D<double> Vector3D<double>::Zero( 0., 0., 0. ); +template<> const Vector3D<float> Vector3D<float>::Invalid( numeric_limits<float>::quiet_NaN(), numeric_limits<float>::quiet_NaN(), numeric_limits<float>::quiet_NaN() ); +template<> const Vector3D<double> Vector3D<double>::Invalid( numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN() ); +//template<> const Vector3D<int> Vector3D<int>::Invalid( -1, -1, -1 ); +template<> bool Vector3D<float>::isValid() const { return !c_isnan(x) && !c_isnan(y) && !c_isnan(z); } +template<> bool Vector3D<double>::isValid() const { return !c_isnan(x) && !c_isnan(y) && !c_isnan(z); } +//template<> bool Vector3D<int>::isValid() const { return x!=-1 || y!=-1 || z!=-1; } + + + + +}
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/util/vectorbase.h b/source/blender/python/manta_full/source/util/vectorbase.h new file mode 100644 index 00000000000..9fca17703ba --- /dev/null +++ b/source/blender/python/manta_full/source/util/vectorbase.h @@ -0,0 +1,585 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic vector class + * + ******************************************************************************/ + +#ifndef _VECTORBASE_H +#define _VECTORBASE_H + +// get rid of windos min/max defines +#if defined(WIN32) || defined(_WIN32) +# define NOMINMAX +#endif + +#include <stdio.h> +//#include <stdlib.h> +#include <string> +#include <cmath> +#include <iostream> +#include "general.h" + +// if min/max are still around... +#if defined(WIN32) || defined(_WIN32) +# undef min +# undef max +#endif + +// redefine usage of some windows functions +#if defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +// use which fp-precision? 1=float, 2=double +#ifndef FLOATINGPOINT_PRECISION +# define FLOATINGPOINT_PRECISION 1 +#endif + +// VECTOR_EPSILON is the minimal vector length +// In order to be able to discriminate floating point values near zero, and +// to be sure not to fail a comparison because of roundoff errors, use this +// value as a threshold. +#if FLOATINGPOINT_PRECISION==1 + typedef float Real; +# define FP_REAL_MAX __FLT_MAX__ +# define VECTOR_EPSILON (1e-6f) +# define _0 0.0f +# define _1 1.0f +#else + typedef double Real; +# define FP_REAL_MAX __DBL_MAX__ +# define VECTOR_EPSILON (1e-10) +# define _0 0.0 +# define _1 1.0 +#endif + +// windos, hardcoded limits for now... +// for e.g. MSVC compiler... +// some of these defines can be needed +// for linux systems as well (e.g. FLT_MAX) +#ifndef __FLT_MAX__ +# ifdef FLT_MAX // try to use it instead +# define __FLT_MAX__ FLT_MAX +# else // FLT_MAX +# define __FLT_MAX__ 3.402823466e+38f +# endif // FLT_MAX +#endif // __FLT_MAX__ +#ifndef __DBL_MAX__ +# ifdef DBL_MAX // try to use it instead +# define __DBL_MAX__ DBL_MAX +# else // DBL_MAX +# define __DBL_MAX__ 1.7976931348623158e+308 +# endif // DBL_MAX +#endif // __DBL_MAX__ + +#ifndef M_PI +# define M_PI 3.1415926536 +# define M_E 2.7182818284 +#endif + +namespace Manta +{ + +//! Basic inlined vector class +template<class S> +class Vector3D +{ +public: + //! Constructor + inline Vector3D() : x(0),y(0),z(0) {} + + //! Copy-Constructor + inline Vector3D ( const Vector3D<S> &v ) : x(v.x), y(v.y), z(v.z) {} + + //! Copy-Constructor + inline Vector3D ( const float * v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) {} + + //! Copy-Constructor + inline Vector3D ( const double * v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) {} + + //! Construct a vector from one S + inline Vector3D ( S v) : x(v), y(v), z(v) {} + + //! Construct a vector from three Ss + inline Vector3D ( S vx, S vy, S vz) : x(vx), y(vy), z(vz) {} + + // Operators + + //! Assignment operator + inline const Vector3D<S>& operator= ( const Vector3D<S>& v ) { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + //! Assignment operator + inline const Vector3D<S>& operator= ( S s ) { + x = y = z = s; + return *this; + } + //! Assign and add operator + inline const Vector3D<S>& operator+= ( const Vector3D<S>& v ) { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + //! Assign and add operator + inline const Vector3D<S>& operator+= ( S s ) { + x += s; + y += s; + z += s; + return *this; + } + //! Assign and sub operator + inline const Vector3D<S>& operator-= ( const Vector3D<S>& v ) { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + //! Assign and sub operator + inline const Vector3D<S>& operator-= ( S s ) { + x -= s; + y -= s; + z -= s; + return *this; + } + //! Assign and mult operator + inline const Vector3D<S>& operator*= ( const Vector3D<S>& v ) { + x *= v.x; + y *= v.y; + z *= v.z; + return *this; + } + //! Assign and mult operator + inline const Vector3D<S>& operator*= ( S s ) { + x *= s; + y *= s; + z *= s; + return *this; + } + //! Assign and div operator + inline const Vector3D<S>& operator/= ( const Vector3D<S>& v ) { + x /= v.x; + y /= v.y; + z /= v.z; + return *this; + } + //! Assign and div operator + inline const Vector3D<S>& operator/= ( S s ) { + x /= s; + y /= s; + z /= s; + return *this; + } + //! Negation operator + inline Vector3D<S> operator- () const { + return Vector3D<S> (-x, -y, -z); + } + + //! Get smallest component + inline S min() const { + return ( x<y ) ? ( ( x<z ) ? x:z ) : ( ( y<z ) ? y:z ); + } + //! Get biggest component + inline S max() const { + return ( x>y ) ? ( ( x>z ) ? x:z ) : ( ( y>z ) ? y:z ); + } + + //! Test if all components are zero + inline bool empty() { + return x==0 && y==0 && z==0; + } + + //! access operator + inline S& operator[] ( unsigned int i ) { + return value[i]; + } + //! constant access operator + inline const S& operator[] ( unsigned int i ) const { + return value[i]; + } + + //! debug output vector to a string + std::string toString() const; + + //! test if nans are present + bool isValid() const; + + //! actual values + union { + S value[3]; + struct { + S x; + S y; + S z; + }; + struct { + S X; + S Y; + S Z; + }; + }; + + // zero element + static const Vector3D<S> Zero, Invalid; + +protected: + +}; + +//************************************************************************ +// Additional operators +//************************************************************************ + +//! Addition operator +template<class S> +inline Vector3D<S> operator+ ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x+v2.x, v1.y+v2.y, v1.z+v2.z ); +} +//! Addition operator +template<class S, class S2> +inline Vector3D<S> operator+ ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x+s, v.y+s, v.z+s ); +} +//! Addition operator +template<class S, class S2> +inline Vector3D<S> operator+ ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( v.x+s, v.y+s, v.z+s ); +} + +//! Subtraction operator +template<class S> +inline Vector3D<S> operator- ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x-v2.x, v1.y-v2.y, v1.z-v2.z ); +} +//! Subtraction operator +template<class S, class S2> +inline Vector3D<S> operator- ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x-s, v.y-s, v.z-s ); +} +//! Subtraction operator +template<class S, class S2> +inline Vector3D<S> operator- ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s-v.x, s-v.y, s-v.z ); +} + +//! Multiplication operator +template<class S> +inline Vector3D<S> operator* ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x*v2.x, v1.y*v2.y, v1.z*v2.z ); +} +//! Multiplication operator +template<class S, class S2> +inline Vector3D<S> operator* ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x*s, v.y*s, v.z*s ); +} +//! Multiplication operator +template<class S, class S2> +inline Vector3D<S> operator* ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s*v.x, s*v.y, s*v.z ); +} + +//! Division operator +template<class S> +inline Vector3D<S> operator/ ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x/v2.x, v1.y/v2.y, v1.z/v2.z ); +} +//! Division operator +template<class S, class S2> +inline Vector3D<S> operator/ ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x/s, v.y/s, v.z/s ); +} +//! Division operator +template<class S, class S2> +inline Vector3D<S> operator/ ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s/v.x, s/v.y, s/v.z ); +} + +//! Comparison operator +template<class S> +inline bool operator== (const Vector3D<S>& s1, const Vector3D<S>& s2) { + return s1.x == s2.x && s1.y == s2.y && s1.z == s2.z; +} + +//! Comparison operator +template<class S> +inline bool operator!= (const Vector3D<S>& s1, const Vector3D<S>& s2) { + return s1.x != s2.x || s1.y != s2.y || s1.z != s2.z; +} + +//************************************************************************ +// External functions +//************************************************************************ + +//! Dot product +template<class S> +inline S dot ( const Vector3D<S> &t, const Vector3D<S> &v ) { + return t.x*v.x + t.y*v.y + t.z*v.z; +} + +//! Cross product +template<class S> +inline Vector3D<S> cross ( const Vector3D<S> &t, const Vector3D<S> &v ) { + Vector3D<S> cp ( + ( ( t.y*v.z ) - ( t.z*v.y ) ), + ( ( t.z*v.x ) - ( t.x*v.z ) ), + ( ( t.x*v.y ) - ( t.y*v.x ) ) ); + return cp; +} + +//! Project a vector into a plane, defined by its normal +/*! Projects a vector into a plane normal to the given vector, which must + have unit length. Self is modified. + \param v The vector to project + \param n The plane normal + \return The projected vector */ +template<class S> +inline const Vector3D<S>& projectNormalTo ( const Vector3D<S>& v, const Vector3D<S> &n) { + S sprod = dot (v, n); + return v - n * dot(v, n); +} + +//! Compute the magnitude (length) of the vector +template<class S> +inline S norm ( const Vector3D<S>& v ) { + S l = v.x*v.x + v.y*v.y + v.z*v.z; + return ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) ? 1. : sqrt ( l ); +} + +//! Compute squared magnitude +template<class S> +inline S normSquare ( const Vector3D<S>& v ) { + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +//! Returns a normalized vector +template<class S> +inline Vector3D<S> getNormalized ( const Vector3D<S>& v ) { + S l = v.x*v.x + v.y*v.y + v.z*v.z; + if ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) + return v; /* normalized "enough"... */ + else if ( l > VECTOR_EPSILON*VECTOR_EPSILON ) + { + S fac = 1./sqrt ( l ); + return Vector3D<S> ( v.x*fac, v.y*fac, v.z*fac ); + } + else + return Vector3D<S> ( ( S ) 0 ); +} + +//! Compute the norm of the vector and normalize it. +/*! \return The value of the norm */ +template<class S> +inline S normalize ( Vector3D<S> &v ) { + S norm; + S l = v.x*v.x + v.y*v.y + v.z*v.z; + if ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) { + norm = 1.; + } else if ( l > VECTOR_EPSILON*VECTOR_EPSILON ) { + norm = sqrt ( l ); + v *= 1./norm; + } else { + v = Vector3D<S>::Zero; + norm = 0.; + } + return ( S ) norm; +} + +//! Obtain an orthogonal vector +/*! Compute a vector that is orthonormal to the given vector. + * Nothing else can be assumed for the direction of the new vector. + * \return The orthonormal vector */ +template<class S> +Vector3D<S> getOrthogonalVector(const Vector3D<S>& v) { + // Determine the component with max. absolute value + int maxIndex= ( fabs ( v.x ) > fabs ( v.y ) ) ? 0 : 1; + maxIndex= ( fabs ( v[maxIndex] ) > fabs ( v.z ) ) ? maxIndex : 2; + + // Choose another axis than the one with max. component and project + // orthogonal to self + Vector3D<S> o ( 0.0 ); + o[ ( maxIndex+1 ) %3]= 1; + + Vector3D<S> c = cross(v, o); + normalize(c); + return c; +} + +//! Convert vector to polar coordinates +/*! Stable vector to angle conversion + *\param v vector to convert + \param phi unique angle [0,2PI] + \param theta unique angle [0,PI] + */ +template<class S> +inline void vecToAngle ( const Vector3D<S>& v, S& phi, S& theta ) +{ + if ( fabs ( v.y ) < VECTOR_EPSILON ) + theta = M_PI/2; + else if ( fabs ( v.x ) < VECTOR_EPSILON && fabs ( v.z ) < VECTOR_EPSILON ) + theta = ( v.y>=0 ) ? 0:M_PI; + else + theta = atan ( sqrt ( v.x*v.x+v.z*v.z ) /v.y ); + if ( theta<0 ) theta+=M_PI; + + if ( fabs ( v.x ) < VECTOR_EPSILON ) + phi = M_PI/2; + else + phi = atan ( v.z/v.x ); + if ( phi<0 ) phi+=M_PI; + if ( fabs ( v.z ) < VECTOR_EPSILON ) + phi = ( v.x>=0 ) ? 0 : M_PI; + else if ( v.z < 0 ) + phi += M_PI; +} + +//! Compute vector reflected at a surface +/*! Compute a vector, that is self (as an incoming vector) + * reflected at a surface with a distinct normal vector. + * Note that the normal is reversed, if the scalar product with it is positive. + \param t The incoming vector + \param n The surface normal + \return The new reflected vector + */ +template<class S> +inline Vector3D<S> reflectVector ( const Vector3D<S>& t, const Vector3D<S>& n ) { + Vector3D<S> nn= ( dot ( t, n ) > 0.0 ) ? ( n*-1.0 ) : n; + return ( t - nn * ( 2.0 * dot ( nn, t ) ) ); +} + +//! Compute vector refracted at a surface +/*! \param t The incoming vector + * \param n The surface normal + * \param nt The "inside" refraction index + * \param nair The "outside" refraction index + * \param refRefl Set to 1 on total reflection + * \return The refracted vector +*/ +template<class S> +inline Vector3D<S> refractVector ( const Vector3D<S> &t, const Vector3D<S> &normal, S nt, S nair, int &refRefl ) { + // from Glassner's book, section 5.2 (Heckberts method) + S eta = nair / nt; + S n = -dot ( t, normal ); + S tt = 1.0 + eta*eta* ( n*n-1.0 ); + if ( tt<0.0 ) { + // we have total reflection! + refRefl = 1; + } else { + // normal reflection + tt = eta*n - sqrt ( tt ); + return ( t*eta + normal*tt ); + } + return t; +} + +//! Outputs the object in human readable form as string +template<class S> std::string Vector3D<S>::toString() const { + char buf[256]; + snprintf ( buf,256,"[%+4.6f,%+4.6f,%+4.6f]", ( double ) ( *this ) [0], ( double ) ( *this ) [1], ( double ) ( *this ) [2] ); + // for debugging, optionally increase precision: + //snprintf ( buf,256,"[%+4.16f,%+4.16f,%+4.16f]", ( double ) ( *this ) [0], ( double ) ( *this ) [1], ( double ) ( *this ) [2] ); + return std::string ( buf ); +} + +//! Outputs the object in human readable form to stream +/*! Output format [x,y,z] */ +template<class S> +std::ostream& operator<< ( std::ostream& os, const Vector3D<S>& i ) { + os << i.toString(); + return os; +} + +//! Reads the contents of the object from a stream +/*! Input format [x,y,z] */ +template<class S> +std::istream& operator>> ( std::istream& is, Vector3D<S>& i ) { + char c; + char dummy[3]; + is >> c >> i[0] >> dummy >> i[1] >> dummy >> i[2] >> c; + return is; +} + +/**************************************************************************/ +// Define default vector alias +/**************************************************************************/ + +//! 3D vector class of type Real (typically float) +typedef Vector3D<Real> Vec3; + +//! 3D vector class of type int +typedef Vector3D<int> Vec3i; + +//! convert to Real Vector +template<class T> inline Vec3 toVec3 ( T v ) { + return Vec3 ( v[0],v[1],v[2] ); +} + +//! convert to int Vector +template<class T> inline Vec3i toVec3i ( T v ) { + return Vec3i ( ( int ) v[0], ( int ) v[1], ( int ) v[2] ); +} + +//! convert to int Vector +template<class T> inline Vec3i toVec3i ( T v0, T v1, T v2 ) { + return Vec3i ( ( int ) v0, ( int ) v1, ( int ) v2 ); +} + +//! round, and convert to int Vector +template<class T> inline Vec3i toVec3iRound ( T v ) { + return Vec3i ( ( int ) round ( v[0] ), ( int ) round ( v[1] ), ( int ) round ( v[2] ) ); +} + +//! convert to int Vector if values are close enough to an int +template<class T> inline Vec3i toVec3iChecked ( T v ) { + Vec3i ret; + for (size_t i=0; i<3; i++) { + Real a = v[i]; + if (fabs(a-floor(a+0.5)) > 1e-5) + errMsg("argument is not an int, cannot convert"); + ret[i] = (int) (a+0.5); + } + return ret; +} + +//! convert to double Vector +template<class T> inline Vector3D<double> toVec3d ( T v ) { + return Vector3D<double> ( v[0], v[1], v[2] ); +} + +//! convert to float Vector +template<class T> inline Vector3D<float> toVec3f ( T v ) { + return Vector3D<float> ( v[0], v[1], v[2] ); +} + + +/**************************************************************************/ +// Specializations for common math functions +/**************************************************************************/ + +template<> inline Vec3 clamp<Vec3>(const Vec3& a, const Vec3& b, const Vec3& c) { + return Vec3 ( clamp(a.x, b.x, c.x), + clamp(a.y, b.y, c.y), + clamp(a.z, b.z, c.z) ); +} +template<> inline Vec3 safeDivide<Vec3>(const Vec3 &a, const Vec3& b) { + return Vec3(safeDivide(a.x,b.x), safeDivide(a.y,b.y), safeDivide(a.z,b.z)); +} +template<> inline Vec3 nmod<Vec3>(const Vec3& a, const Vec3& b) { + return Vec3(nmod(a.x,b.x),nmod(a.y,b.y),nmod(a.z,b.z)); +} + +}; // namespace + + +#endif diff --git a/source/blender/python/manta_full/source/vortexfilament.cpp b/source/blender/python/manta_full/source/vortexfilament.cpp new file mode 100644 index 00000000000..77d467313cd --- /dev/null +++ b/source/blender/python/manta_full/source/vortexfilament.cpp @@ -0,0 +1,340 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex filament + * + ******************************************************************************/ + +#include "vortexfilament.h" +#include "integrator.h" +#include "interpol.h" +#include "mesh.h" +#include "quaternion.h" + +using namespace std; +namespace Manta { + +void VortexRing::renumber(int *_renumber) { + for (size_t i=0; i<indices.size(); i++) + indices[i] = _renumber[indices[i]]; +} + +inline Vec3 FilamentKernel(const Vec3& pos, const vector<VortexRing>& rings, const vector<BasicParticleData>& fp, Real reg, Real cutoff, Real scale) { + const Real strength = 0.25 / M_PI * scale; + const Real a2 = square(reg); + const Real cutoff2 = square(cutoff); + const Real mindist = 1e-6; + Vec3 u(_0); + + for (size_t i=0; i<rings.size(); i++) { + const VortexRing& r = rings[i]; + if (r.flag & ParticleBase::PDELETE) continue; + + const int N = r.isClosed ? (r.size()) : (r.size()-1); + const Real str = strength * r.circulation; + for (int j=0; j<N; j++) { + const Vec3 r0 = fp[r.idx0(j)].pos - pos; + const Vec3 r1 = fp[r.idx1(j)].pos - pos; + const Real r0_2 = normSquare(r0), r1_2 = normSquare(r1); + if (r0_2 > cutoff2 || r1_2 > cutoff2 || r0_2 < mindist || r1_2 < mindist) + continue; + + const Vec3 e = getNormalized(r1-r0); + const Real r0n = 1.0f/sqrt(a2+r0_2); + const Real r1n = 1.0f/sqrt(a2+r1_2); + const Vec3 cp = cross(r0,e); + const Real A = str * (dot(r1,e)*r1n - dot(r0,e)*r0n) / (a2 + normSquare(cp)); + u += A * cp; + } + } + return u; +} + +KERNEL(pts) returns(vector<Vec3> u(size)) +vector<Vec3> KnFilamentAdvectParts(vector<BasicParticleData>& nodes, vector<BasicParticleData>& fp, const vector<VortexRing>& rings, Real reg, Real cutoff, Real scale) { + if (nodes[idx].flag & ParticleBase::PDELETE) + u[idx] = _0; + else + u[idx] = FilamentKernel(nodes[idx].pos, rings, fp, reg, cutoff, scale); +} + +KERNEL(pts) returns(vector<Vec3> u(size)) +vector<Vec3> KnFilamentAdvectMesh(vector<Node>& nodes, const vector<VortexRing>& rings, const vector<BasicParticleData>& fp, Real reg, Real cutoff, Real scale) { + if (nodes[idx].flags & Mesh::NfFixed) + u[idx] = _0; + else + u[idx] = FilamentKernel(nodes[idx].pos, rings, fp, reg, cutoff, scale); +} + +void VortexFilamentSystem::advectSelf(Real scale, Real regularization, int integrationMode) { + KnFilamentAdvectParts kernel(mData, mData, mSegments , regularization, 1e10, scale * getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void VortexFilamentSystem::advectMesh(Mesh& mesh, Real scale, Real regularization, int integrationMode) { + KnFilamentAdvectMesh kernel(mesh.getNodeData(), mSegments, mData, regularization, 1e10, scale * getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void VortexFilamentSystem::advectParticles(BasicParticleSystem& sys, Real scale, Real regularization, int integrationMode) { + KnFilamentAdvectParts kernel(sys.getData(), mData, mSegments, regularization, 1e10, scale * getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void VortexFilamentSystem::remesh(Real maxLen, Real minLen) { + const Real maxLen2 = maxLen*maxLen, minLen2 = minLen*minLen; + + for (int i=0; i < segSize(); i++) { + VortexRing& r = mSegments[i]; + + // insert edges + for(;;) { + const int oldLen = r.size(); + map<int,int> insert; + int offset = 1; + + for (int j=0; j<oldLen; j++) { + const Vec3 p0 = mData[r.idx0(j)].pos; + const Vec3 p1 = mData[r.idx1(j)].pos; + const Real l2 = normSquare(p1-p0); + + if (l2 > maxLen2) { + // insert midpoint + const Vec3 p_1 = mData[r.idx(j-1)].pos; + const Vec3 p2 = mData[r.idx(j+2)].pos; + const Vec3 mp = hermiteSpline(p0,p1,crTangent(p_1,p0,p1),crTangent(p0,p1,p2), 0.5); + insert.insert(pair<int,int>(j+offset, add(mp))); + offset++; + } + } + if (insert.empty()) + break; + + // renumber indices + const int newLen = oldLen + insert.size(); + int num=oldLen-1; + r.indices.resize(newLen); + for (int j=newLen-1; j>=0; j--) { + map<int,int>::const_iterator f = insert.find(j); + if (f==insert.end()) + r.indices[j] = r.indices[num--]; + else + r.indices[j] = f->second; + } + } + + // remove edges + for(;;) { + const int oldLen = r.size(); + const int N = r.isClosed ? oldLen : (oldLen-1); + std::vector<bool> deleted(r.size()); + + int newLen=oldLen; + for (int j=0; j<N; j++) { + if (mData[r.idx0(j)].flag & PDELETE || mData[r.idx1(j)].flag & PDELETE) continue; + const Vec3 p0 = mData[r.idx0(j)].pos; + const Vec3 p1 = mData[r.idx1(j)].pos; + const Real l2 = normSquare(p1-p0); + + if (l2 < minLen2) { + // kill edge + mData[r.idx0(j)].flag |= PDELETE; + mData[r.idx1(j)].pos = 0.5*(p0+p1); + deleted[j] = true; + newLen--; + j++; + } + } + if (newLen == oldLen) + break; + + // renumber indices + for (int j=0, copyFrom=0; j<newLen; j++,copyFrom++) { + while (deleted[copyFrom]) + copyFrom++; + if (j!=copyFrom) + r.indices[j] = r.indices[copyFrom]; + } + r.indices.resize(newLen); + } + } + + // remove deleted particles + compress(); +} + +VortexFilamentSystem::VortexFilamentSystem(FluidSolver* parent) : + ConnectedParticleSystem<BasicParticleData, VortexRing>(parent) +{ +} + +ParticleBase* VortexFilamentSystem::clone() { + VortexFilamentSystem* nm = new VortexFilamentSystem(getParent()); + compress(); + + nm->mData = mData; + nm->mSegments = mSegments; + nm->setName(getName()); + return nm; +} + +// ------------------------------------------------------------------------------ +// Functions needed for doubly-discrete smoke flow using Darboux transforms +// see [Weissmann,Pinkall 2009] +// doesn't really work yet (can't reverse rotation dir) +// ------------------------------------------------------------------------------ + +Real evaluateRefU(int N, Real L, Real circ, Real reg) { + // construct regular n-polygon + const Real l = L/(Real)N; + const Real r = 0.5*l/sin(M_PI/(Real)N); + cout << r << " " << l << endl; + // build vortex ring + VortexRing ring (circ); + vector<BasicParticleData> pos(N); + for(int i=0; i<N; i++) { + pos[i].pos = Vec3( r*cos(2.0*M_PI*(Real)i/N), r*sin(2.0*M_PI*(Real)i/N), 0); + pos[i].flag =0; + ring.indices.push_back(i); + } + + // Build kernel + vector<VortexRing> rings; + rings.push_back(ring); + + // evaluate impact on pos[0] + return norm(FilamentKernel(pos[0].pos, rings, pos, reg, 1e10, 1.0)); +} + +Vec3 darbouxStep(const Vec3& Si, const Vec3& lTi, Real r) { + Quaternion rlTS (lTi - Si, -r); + Quaternion lT (lTi, 0); + Quaternion lTnext = rlTS * lT * rlTS.inverse(); + return lTnext.imag(); +} + +Vec3 monodromy(const vector<Vec3>& gamma, const Vec3& lT_1, Real r) { + const int N = gamma.size(); + Vec3 lT (lT_1); + + for (int i=0; i<N; i++) { + Vec3 Si = gamma[(i+1)%N]-gamma[i]; + lT = darbouxStep(Si, lT, r); + } + return lT; +} + +bool powerMethod(const vector<Vec3>& gamma, Real l, Real r, Vec3& lT) { + const int maxIter = 100; + const Real epsilon = 1e-4; + + for (int i=0; i<maxIter; i++) { + Vec3 lastLT (lT); + lT = monodromy(gamma, lT, r); + //if ((i%1) == 0) cout << "iteration " << i << " residual: " << norm(lT-lastLT) << endl; + if (norm(lT-lastLT) < epsilon) + return true; + } + return false; +} + +bool darboux(const vector<Vec3>& from, vector<Vec3>& to, Real l, Real r) { + const int N = from.size(); + Vec3 lT(0,0,l); + if (!powerMethod(from, l, r, lT)) + return false; + cout << "iniLT " << lT << " norm " << lT/l<< endl; + + for (int i=0; i<N; i++) { + to[i] = from[i] + lT; + Vec3 Si = from[(i+1)%N] - from[i]; + lT = darbouxStep(Si, lT, r); + } + return true; +} + + +void VortexFilamentSystem::doublyDiscreteUpdate(Real reg) { + const Real dt = getParent()->getDt(); + + for (int rc=0; rc<segSize(); rc++) { + if (!isSegActive(rc) || !mSegments[rc].isClosed) continue; + + VortexRing& r = mSegments[rc]; + int N = r.size(); + + // compute arc length + Real L=0; + for (int i=0; i<N; i++) + L += norm(mData[r.idx0(i)].pos - mData[r.idx1(i)].pos); + + // build gamma + vector<Vec3> gamma(N); + for (int i=0; i<N; i++) gamma[i] = mData[r.indices[i]].pos; + + //N=1000; L=2.0*M_PI; reg=0.1; r.circulation=1; + + // compute reference parameters + const Real U = 0.5*r.circulation/L * (log(4.0*L/(M_PI*reg)) - 1.0); + const Real Ur = evaluateRefU(N, L, r.circulation, reg); + const Real d = 0.5*dt*(U-Ur); + const Real l = sqrt( square(L/N) + square(d) ); + const Real ra = d*tan(M_PI * (0.5 - 1.0/N)); // d*cot(pi/n) + cout << U << " <-< " << Ur << endl; + + // fwd darboux transform + vector<Vec3> eta(N); + if (!darboux(gamma, eta, l, ra)) { + cout << "Fwd Darboux correction failed, skipped." << endl; + continue; + } + + // bwd darboux transform + if (!darboux(eta, gamma, l, ra)) { + cout << "Bwd Darboux correction failed, skipped." << endl; + continue; + } + + // copy back + for (int i=0; i<N; i++) { + mData[r.indices[i]].pos = gamma[i]; + } + } +} + +void VortexFilamentSystem::addLine(const Vec3& p0, const Vec3& p1, Real circulation) { + VortexRing ring(circulation, false); + + ring.indices.push_back(add(BasicParticleData(p0))); + ring.indices.push_back(add(BasicParticleData(p1))); + mSegments.push_back(ring); +} + +void VortexFilamentSystem::addRing(const Vec3& position, Real circulation, Real radius, Vec3 normal, int number) { + normalize(normal); + Vec3 worldup (0,1,0); + if (norm(normal - worldup) < 1e-5) worldup = Vec3(1,0,0); + + Vec3 u = cross(normal, worldup); normalize(u); + Vec3 v = cross(normal, u); normalize(v); + + VortexRing ring(circulation, true); + + for (int i=0; i<number; i++) { + Real phi = (Real)i/(Real)number * M_PI * 2.0; + Vec3 p = position + radius * (u*cos(phi) + v*sin(phi)); + + int num = add(BasicParticleData(p)); + ring.indices.push_back(num); + } + mSegments.push_back(ring); +} + + +} // namespace diff --git a/source/blender/python/manta_full/source/vortexfilament.h b/source/blender/python/manta_full/source/vortexfilament.h new file mode 100644 index 00000000000..08faf338897 --- /dev/null +++ b/source/blender/python/manta_full/source/vortexfilament.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex filament + * + ******************************************************************************/ + +#ifndef _VORTEXFIL_H +#define _VORTEXFIL_H + +#include "particle.h" + +namespace Manta { +class Mesh; + +struct VortexRing { + VortexRing() : isClosed(false),circulation(0.),flag(0) {} + VortexRing(Real c, bool closed=false) : isClosed(closed),circulation(c),flag(0) {} + void renumber(int* _renumber); + inline int size() const { return indices.size(); } + inline int idx(int i) const { return indices[(i+indices.size()) % indices.size()]; } + inline int idx0(int i) const { return indices[i]; } + inline int idx1(int i) const { return indices[ (i+1) % indices.size() ]; } + + Real circulation; + bool isClosed; + int flag; + std::vector<int> indices; +}; + +//! Vortex filaments +PYTHON class VortexFilamentSystem : public ConnectedParticleSystem<BasicParticleData, VortexRing> { +public: + virtual SystemType getType() const { return ParticleBase::FILAMENT; }; + + PYTHON VortexFilamentSystem(FluidSolver* parent); + + //! self-advect the filament system + PYTHON void advectSelf(Real scale=1.0, Real regularization=0.1, int integrationMode=IntRK4); + //! advect a particle system + PYTHON void advectParticles(BasicParticleSystem& sys, Real scale=1.0, Real regularization=0.1, int integrationMode=IntRK2); + //! advect triangle mesh using filaments + PYTHON void advectMesh(Mesh& mesh, Real scale=1.0, Real regularization=0.1, int integrationMode=IntRK4); + //! perform doubly-discrete smoke ring flow update + //! as in [Weissmann,Pinkall 2009] + PYTHON void doublyDiscreteUpdate(Real regularization=0.1); + //! remesh long or strongly-curved segments + PYTHON void remesh(Real maxLen=3.0, Real minLen=1.0); + + //! add a filament ring to the system + PYTHON void addRing(const Vec3& position, Real circulation, Real radius, Vec3 normal, int number); + //! add a line filament to the system + PYTHON void addLine(const Vec3& p0, const Vec3& p1, Real circulation); + + + virtual ParticleBase* clone(); +protected: + + //! Biot-Savart line integration + void integrate(const std::vector<Vec3>& nodesOld, std::vector<Vec3>& nodesNew, Real scale, Real reg, int integrationMode); +}; + +} // namespace + + +#endif diff --git a/source/blender/python/manta_full/source/vortexpart.cpp b/source/blender/python/manta_full/source/vortexpart.cpp new file mode 100644 index 00000000000..2b297e9f337 --- /dev/null +++ b/source/blender/python/manta_full/source/vortexpart.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex particles + * + ******************************************************************************/ + +#include "vortexpart.h" +#include "integrator.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +// vortex particle effect: (cyl coord around wp) +// u = -|wp|*rho*exp( (-rho^2-z^2)/(2sigma^2) ) e_phi +inline Vec3 VortexKernel(const Vec3& p, const vector<VortexParticleData>& vp, Real scale) { + Vec3 u(_0); + for (size_t i=0; i<vp.size(); i++) { + if (vp[i].flag & ParticleBase::PDELETE) continue; + + // cutoff radius + const Vec3 r = p - vp[i].pos; + const Real rlen2 = normSquare(r); + const Real sigma2 = square(vp[i].sigma); + if (rlen2 > 6.0 * sigma2 || rlen2 < 1e-8) continue; + + // split vortex strength + Vec3 vortNorm = vp[i].vorticity; + Real strength = normalize(vortNorm) * scale; + + // transform in cylinder coordinate system + const Real rlen = sqrt(rlen2); + const Real z = dot(r, vortNorm); + const Vec3 ePhi = cross(r, vortNorm) / rlen; + const Real rho2 = rlen2 - z*z; + + Real vortex = 0; + if (rho2 > 1e-10) { + // evaluate Kernel + vortex = strength * sqrt(rho2) * exp (rlen2 * -0.5/sigma2); + } + u += vortex * ePhi; + } + return u; +} + +KERNEL(pts) returns(vector<Vec3> u(size)) +vector<Vec3> KnVpAdvectMesh(vector<Node>& nodes, const vector<VortexParticleData>& vp, Real scale) { + if (nodes[idx].flags & Mesh::NfFixed) + u[idx] = _0; + else + u[idx] = VortexKernel(nodes[idx].pos, vp, scale); +} + +KERNEL(pts) returns(vector<Vec3> u(size)) +vector<Vec3> KnVpAdvectSelf(vector<VortexParticleData>& vp, Real scale) { + if (vp[idx].flag & ParticleBase::PDELETE) + u[idx] = _0; + else + u[idx] = VortexKernel(vp[idx].pos, vp, scale); +} + +VortexParticleSystem::VortexParticleSystem(FluidSolver* parent) : + ParticleSystem<VortexParticleData>(parent) +{ +} + +void VortexParticleSystem::advectSelf(Real scale, int integrationMode) { + KnVpAdvectSelf kernel(mData, scale* getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void VortexParticleSystem::applyToMesh(Mesh& mesh, Real scale, int integrationMode) { + KnVpAdvectMesh kernel(mesh.getNodeData(), mData, scale* getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +ParticleBase* VortexParticleSystem::clone() { + VortexParticleSystem* nm = new VortexParticleSystem(getParent()); + compress(); + + nm->mData = mData; + nm->setName(getName()); + return nm; +} + + + +} // namespace diff --git a/source/blender/python/manta_full/source/vortexpart.h b/source/blender/python/manta_full/source/vortexpart.h new file mode 100644 index 00000000000..c81c1a8f26c --- /dev/null +++ b/source/blender/python/manta_full/source/vortexpart.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex particles + * + ******************************************************************************/ + +#ifndef _VORTEXPART_H +#define _VORTEXPART_H + +#include "particle.h" + +namespace Manta { +class Mesh; + +struct VortexParticleData { + VortexParticleData() : pos(_0),vorticity(_0),sigma(0),flag(0) {} + VortexParticleData(const Vec3& p, const Vec3& v, Real sig) : pos(p),vorticity(v),sigma(sig),flag(0) {} + Vec3 pos, vorticity; + Real sigma; + int flag; + static ParticleBase::SystemType getType() { return ParticleBase::VORTEX; } +}; + +//! Vortex particles +PYTHON class VortexParticleSystem : public ParticleSystem<VortexParticleData> { +public: + PYTHON VortexParticleSystem(FluidSolver* parent); + + PYTHON void advectSelf(Real scale=1.0, int integrationMode=IntRK4); + PYTHON void applyToMesh(Mesh& mesh, Real scale=1.0, int integrationMode=IntRK4); + + virtual ParticleBase* clone(); +}; + +} // namespace + + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_full/source/vortexsheet.cpp b/source/blender/python/manta_full/source/vortexsheet.cpp new file mode 100644 index 00000000000..dc753e87ff5 --- /dev/null +++ b/source/blender/python/manta_full/source/vortexsheet.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex sheets + * + ******************************************************************************/ + +#include "vortexsheet.h" +#include "solvana.h" + +using namespace std; +namespace Manta { + +// ***************************************************************************** +// VorticityChannel class members + + + +// ***************************************************************************** +// VortexSheet Mesh class members + +VortexSheetMesh::VortexSheetMesh(FluidSolver* parent) : Mesh(parent), mTexOffset(0.0f) { + addTriChannel(&mVorticity); + addNodeChannel(&mTex1); + addNodeChannel(&mTex2); + addNodeChannel(&mTurb); +} + + +Mesh* VortexSheetMesh::clone() { + VortexSheetMesh* nm = new VortexSheetMesh(mParent); + *nm = *this; + nm->setName(getName()); + return nm; +} + + +void VortexSheetMesh::calcVorticity() { + for (size_t tri = 0; tri < mTris.size(); tri++) { + VortexSheetInfo& v = mVorticity.data[tri]; + Vec3 e0 = getEdge(tri,0), e1 = getEdge(tri,1), e2 = getEdge(tri,2); + Real area = getFaceArea(tri); + + if (area < 1e-10) { + v.smokeAmount = 0; + v.vorticity = 0; + } else { + v.smokeAmount = 0; + v.vorticity = (v.circulation[0]*e0 + v.circulation[1]*e1 + v.circulation[2]*e2) / area; + } + } +} + +void VortexSheetMesh::calcCirculation() { + for (size_t tri = 0; tri < mTris.size(); tri++) { + VortexSheetInfo& v = mVorticity.data[tri]; + Vec3 e0 = getEdge(tri,0), e1 = getEdge(tri,1), e2 = getEdge(tri,2); + Real area = getFaceArea(tri); + + if (area < 1e-10 || normSquare(v.vorticity) < 1e-10) { + v.circulation = 0; + continue; + } + + float cx, cy, cz; + SolveOverconstraint34(e0.x, e0.y, e0.z, e1.x, e1.y, e1.z, e2.x, e2.y, e2.z, v.vorticity.x, v.vorticity.y, v.vorticity.z, cx, cy, cz); + v.circulation = Vec3(cx, cy, cz) * area; + } +} + +void VortexSheetMesh::resetTex1() { + for (size_t i=0; i<mNodes.size(); i++) + mTex1.data[i] = mNodes[i].pos + mTexOffset; +} + +void VortexSheetMesh::resetTex2() { + for (size_t i=0; i<mNodes.size(); i++) + mTex2.data[i] = mNodes[i].pos + mTexOffset; +} + +void VortexSheetMesh::reinitTexCoords() { + resetTex1(); + resetTex2(); +} + +}; // namespace diff --git a/source/blender/python/manta_full/source/vortexsheet.h b/source/blender/python/manta_full/source/vortexsheet.h new file mode 100644 index 00000000000..29dd6b7b887 --- /dev/null +++ b/source/blender/python/manta_full/source/vortexsheet.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex sheets + * + ******************************************************************************/ + +#ifndef _VORTEXSHEET_H +#define _VORTEXSHEET_H + +#include "mesh.h" + +namespace Manta { + +//! Stores vortex sheet info +struct VortexSheetInfo { + VortexSheetInfo() : vorticity(0.0), vorticitySmoothed(0.0), circulation(0.0), smokeAmount(1.0), smokeParticles(0.0) {} + + Vec3 vorticity; + Vec3 vorticitySmoothed; + Vec3 circulation; + Real smokeAmount, smokeParticles; +}; + +//! Manages vortex sheet info +struct VorticityChannel : public SimpleTriChannel<VortexSheetInfo> { + virtual TriChannel* clone() { VorticityChannel* vc = new VorticityChannel(); *vc = *this; return vc;} +}; + +//! Manages 3D texture coordinates +struct TexCoord3Channel : public SimpleNodeChannel<Vec3> { + virtual NodeChannel* clone() { TexCoord3Channel* tc = new TexCoord3Channel(); *tc = *this; return tc; } + + void addInterpol(int a, int b, Real alpha) { data.push_back((1.0-alpha)*data[a] + alpha*data[b]);} + void mergeWith(int node, int delnode, Real alpha) { data[node] = 0.5*(data[node]+data[delnode]); } +}; + +struct TurbulenceInfo { + TurbulenceInfo() : k(0.0), epsilon(0.0) {} + TurbulenceInfo(const TurbulenceInfo& a, const TurbulenceInfo& b, Real alpha) : k((1.0-alpha)*a.k+alpha*b.k), epsilon((1.0-alpha)*a.epsilon+alpha*b.epsilon) {} + Real k, epsilon; +}; + +//! Manages k-epsilon information +struct TurbulenceChannel : public SimpleNodeChannel<TurbulenceInfo> { + virtual NodeChannel* clone() { TurbulenceChannel* tc = new TurbulenceChannel(); *tc = *this; return tc; } + + void addInterpol(int a, int b, Real alpha) { data.push_back(TurbulenceInfo(data[a], data[b], alpha)); } + void mergeWith(int node, int delnode, Real alpha) { data[node] = TurbulenceInfo(data[node], data[delnode], 0.5); } +}; + +//! Typed Mesh with a vorticity and 2 texcoord3 channels +PYTHON class VortexSheetMesh : public Mesh { +public: + PYTHON VortexSheetMesh(FluidSolver* parent); + virtual Mesh* clone(); + + virtual MeshType getType() { return TypeVortexSheet; } + + inline VortexSheetInfo& sheet(int i) { return mVorticity.data[i]; }; + inline Vec3& tex1(int i) { return mTex1.data[i]; } + inline Vec3& tex2(int i) { return mTex2.data[i]; } + inline TurbulenceInfo& turb(int i) { return mTurb.data[i]; } + void setReferenceTexOffset(const Vec3& ref) { mTexOffset = ref; } + void resetTex1(); + void resetTex2(); + + PYTHON void calcCirculation(); + PYTHON void calcVorticity(); + PYTHON void reinitTexCoords(); + +protected: + Vec3 mTexOffset; + VorticityChannel mVorticity; + TexCoord3Channel mTex1, mTex2; + TurbulenceChannel mTurb; +}; + +}; // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/CMakeLists.txt b/source/blender/python/manta_pp/CMakeLists.txt new file mode 100644 index 00000000000..73027c8aa8c --- /dev/null +++ b/source/blender/python/manta_pp/CMakeLists.txt @@ -0,0 +1,137 @@ +#add_definitions(-DGUI=1) + +set(INC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/python + ${CMAKE_CURRENT_SOURCE_DIR}/plugin + ${CMAKE_CURRENT_SOURCE_DIR}/pwrapper + ${CMAKE_CURRENT_SOURCE_DIR}/util + #gui +) + +set(INC_SYS + ${ZLIB_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIRS} +) + +#message("MANTA_processing ${CMAKE_CURRENT_SOURCE_DIR}/commonkernels.h") +set(SRC +#PP SOURCES and HEADERS +${CMAKE_CURRENT_SOURCE_DIR}/commonkernels.h +${CMAKE_CURRENT_SOURCE_DIR}/commonkernels.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/commonkernels.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/conjugategrad.cpp +${CMAKE_CURRENT_SOURCE_DIR}/conjugategrad.h +${CMAKE_CURRENT_SOURCE_DIR}/conjugategrad.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/conjugategrad.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/edgecollapse.cpp +${CMAKE_CURRENT_SOURCE_DIR}/edgecollapse.h +${CMAKE_CURRENT_SOURCE_DIR}/edgecollapse.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/edgecollapse.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fastmarch.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fastmarch.h +${CMAKE_CURRENT_SOURCE_DIR}/fastmarch.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/fastmarch.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fileio.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fileio.h +${CMAKE_CURRENT_SOURCE_DIR}/fileio.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/fileio.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fluidsolver.cpp +${CMAKE_CURRENT_SOURCE_DIR}/fluidsolver.h +${CMAKE_CURRENT_SOURCE_DIR}/fluidsolver.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/fluidsolver.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/general.cpp +${CMAKE_CURRENT_SOURCE_DIR}/general.h +${CMAKE_CURRENT_SOURCE_DIR}/general.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/general.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/grid.cpp +${CMAKE_CURRENT_SOURCE_DIR}/grid.h +${CMAKE_CURRENT_SOURCE_DIR}/grid.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/grid.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/hginfo.h +${CMAKE_CURRENT_SOURCE_DIR}/kernel.cpp +${CMAKE_CURRENT_SOURCE_DIR}/kernel.h +${CMAKE_CURRENT_SOURCE_DIR}/kernel.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/kernel.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/levelset.cpp +${CMAKE_CURRENT_SOURCE_DIR}/levelset.h +${CMAKE_CURRENT_SOURCE_DIR}/levelset.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/levelset.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/mesh.cpp +${CMAKE_CURRENT_SOURCE_DIR}/mesh.h +${CMAKE_CURRENT_SOURCE_DIR}/mesh.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/mesh.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/movingobs.cpp +${CMAKE_CURRENT_SOURCE_DIR}/movingobs.h +${CMAKE_CURRENT_SOURCE_DIR}/movingobs.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/movingobs.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/noisefield.cpp +${CMAKE_CURRENT_SOURCE_DIR}/noisefield.h +${CMAKE_CURRENT_SOURCE_DIR}/noisefield.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/noisefield.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/particle.cpp +${CMAKE_CURRENT_SOURCE_DIR}/particle.h +${CMAKE_CURRENT_SOURCE_DIR}/particle.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/particle.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/advection.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/extforces.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/flip.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/initplugins.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/kepsilon.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/meshplugins.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/pressure.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/vortexplugins.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/waveletturbulence.cpp +${CMAKE_CURRENT_SOURCE_DIR}/plugin/waves.cpp +${CMAKE_CURRENT_SOURCE_DIR}/python/defines.py +${CMAKE_CURRENT_SOURCE_DIR}/python/defines.py.reg +${CMAKE_CURRENT_SOURCE_DIR}/python/defines.py.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/shapes.cpp +${CMAKE_CURRENT_SOURCE_DIR}/shapes.h +${CMAKE_CURRENT_SOURCE_DIR}/shapes.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/shapes.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/test.cpp +${CMAKE_CURRENT_SOURCE_DIR}/timing.cpp +${CMAKE_CURRENT_SOURCE_DIR}/timing.h +${CMAKE_CURRENT_SOURCE_DIR}/timing.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/timing.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/turbulencepart.cpp +${CMAKE_CURRENT_SOURCE_DIR}/turbulencepart.h +${CMAKE_CURRENT_SOURCE_DIR}/turbulencepart.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/turbulencepart.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/vortexpart.cpp +${CMAKE_CURRENT_SOURCE_DIR}/vortexpart.h +${CMAKE_CURRENT_SOURCE_DIR}/vortexpart.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/vortexpart.h.reg.cpp +${CMAKE_CURRENT_SOURCE_DIR}/vortexsheet.cpp +${CMAKE_CURRENT_SOURCE_DIR}/vortexsheet.h +${CMAKE_CURRENT_SOURCE_DIR}/vortexsheet.h.reg +${CMAKE_CURRENT_SOURCE_DIR}/vortexsheet.h.reg.cpp + +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pymain.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pclass.h +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pclass.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pvec3.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pconvert.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pconvert.h +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/registry.h +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/registry.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/pythonInclude.h +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/manta_api.cpp +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/manta.h +${CMAKE_CURRENT_SOURCE_DIR}/pwrapper/manta_api.h +${CMAKE_CURRENT_SOURCE_DIR}/util/vectorbase.cpp +${CMAKE_CURRENT_SOURCE_DIR}/util/integrator.h +${CMAKE_CURRENT_SOURCE_DIR}/util/vectorbase.h +${CMAKE_CURRENT_SOURCE_DIR}/util/vectorbase.cpp +${CMAKE_CURRENT_SOURCE_DIR}/util/quaternion.h +${CMAKE_CURRENT_SOURCE_DIR}/util/interpol.h +${CMAKE_CURRENT_SOURCE_DIR}/util/interpolHigh.h +${CMAKE_CURRENT_SOURCE_DIR}/util/mcubes.h +${CMAKE_CURRENT_SOURCE_DIR}/util/randomstream.h +${CMAKE_CURRENT_SOURCE_DIR}/util/solvana.h +) +list(REMOVE_DUPLICATES "SRC") +list_assert_duplicates("${SRC}") + +blender_add_lib(bf_python_manta "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/python/manta_pp/commonkernels.h b/source/blender/python/manta_pp/commonkernels.h new file mode 100644 index 00000000000..3d0e7a5d5ff --- /dev/null +++ b/source/blender/python/manta_pp/commonkernels.h @@ -0,0 +1,140 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Common grid kernels + * + ******************************************************************************/ + +#ifndef _COMMONKERNELS_H +#define _COMMONKERNELS_H + +#include "general.h" +#include "kernel.h" +#include "grid.h" + +namespace Manta { + +//! Kernel: Invert real values, if positive and fluid + + + struct InvertCheckFluid : public KernelBase { InvertCheckFluid(FlagGrid& flags, Grid<Real>& grid) : KernelBase(&flags,0) ,flags(flags),grid(grid) { run(); } inline void op(int idx, FlagGrid& flags, Grid<Real>& grid ) { + if (flags.isFluid(idx) && grid[idx] > 0) + grid[idx] = 1.0 / grid[idx]; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return grid; } typedef Grid<Real> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, flags,grid); } FlagGrid& flags; Grid<Real>& grid; }; + +//! Kernel: Squared sum over grid + + struct GridSumSqr : public KernelBase { GridSumSqr(Grid<Real>& grid) : KernelBase(&grid,0) ,grid(grid) ,sum(0) { run(); } inline void op(int idx, Grid<Real>& grid ,double& sum) { + sum += square((double)grid[idx]); +} inline operator double () { return sum; } inline double & getRet() { return sum; } inline Grid<Real>& getArg0() { return grid; } typedef Grid<Real> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, grid,sum); } Grid<Real>& grid; double sum; }; + +//! Kernel: rotation operator \nabla x v for centered vector fields + + struct CurlOp : public KernelBase { CurlOp(const Grid<Vec3>& grid, Grid<Vec3>& dst) : KernelBase(&grid,1) ,grid(grid),dst(dst) { run(); } inline void op(int i, int j, int k, const Grid<Vec3>& grid, Grid<Vec3>& dst ) { + Vec3 v = Vec3(0. , 0. , + 0.5*((grid(i+1,j,k).y - grid(i-1,j,k).y) - (grid(i,j+1,k).x - grid(i,j-1,k).x)) ); + if(dst.is3D()) { + v[0] = 0.5*((grid(i,j+1,k).z - grid(i,j-1,k).z) - (grid(i,j,k+1).y - grid(i,j,k-1).y)); + v[1] = 0.5*((grid(i,j,k+1).x - grid(i,j,k-1).x) - (grid(i+1,j,k).z - grid(i-1,j,k).z)); + } + dst(i,j,k) = v; +} inline const Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline Grid<Vec3>& getArg1() { return dst; } typedef Grid<Vec3> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, grid,dst); } const Grid<Vec3>& grid; Grid<Vec3>& dst; };; + +//! Kernel: divergence operator (from MAC grid) + + struct DivergenceOpMAC : public KernelBase { DivergenceOpMAC(Grid<Real>& div, const MACGrid& grid) : KernelBase(&div,1) ,div(div),grid(grid) { run(); } inline void op(int i, int j, int k, Grid<Real>& div, const MACGrid& grid ) { + Vec3 del = Vec3(grid(i+1,j,k).x, grid(i,j+1,k).y, 0.) - grid(i,j,k); + if(grid.is3D()) del[2] += grid(i,j,k+1).z; + else del[2] = 0.; + div(i,j,k) = del.x + del.y + del.z; +} inline Grid<Real>& getArg0() { return div; } typedef Grid<Real> type0;inline const MACGrid& getArg1() { return grid; } typedef MACGrid type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, div,grid); } Grid<Real>& div; const MACGrid& grid; }; + +//! Kernel: gradient operator for MAC grid + struct GradientOpMAC : public KernelBase { GradientOpMAC(MACGrid& gradient, const Grid<Real>& grid) : KernelBase(&gradient,1) ,gradient(gradient),grid(grid) { run(); } inline void op(int i, int j, int k, MACGrid& gradient, const Grid<Real>& grid ) { + Vec3 grad = (Vec3(grid(i,j,k)) - Vec3(grid(i-1,j,k), grid(i,j-1,k), 0. )); + if(grid.is3D()) grad[2] -= grid(i,j,k-1); + else grad[2] = 0.; + gradient(i,j,k) = grad; +} inline MACGrid& getArg0() { return gradient; } typedef MACGrid type0;inline const Grid<Real>& getArg1() { return grid; } typedef Grid<Real> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, gradient,grid); } MACGrid& gradient; const Grid<Real>& grid; }; + +//! Kernel: centered gradient operator + struct GradientOp : public KernelBase { GradientOp(Grid<Vec3>& gradient, const Grid<Real>& grid) : KernelBase(&gradient,1) ,gradient(gradient),grid(grid) { run(); } inline void op(int i, int j, int k, Grid<Vec3>& gradient, const Grid<Real>& grid ) { + Vec3 grad = 0.5 * Vec3( grid(i+1,j,k)-grid(i-1,j,k), + grid(i,j+1,k)-grid(i,j-1,k), 0.); + if(grid.is3D()) grad[2]= 0.5*( grid(i,j,k+1)-grid(i,j,k-1) ); + gradient(i,j,k) = grad; +} inline Grid<Vec3>& getArg0() { return gradient; } typedef Grid<Vec3> type0;inline const Grid<Real>& getArg1() { return grid; } typedef Grid<Real> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, gradient,grid); } Grid<Vec3>& gradient; const Grid<Real>& grid; }; + +//! Kernel: Laplace operator + struct LaplaceOp : public KernelBase { LaplaceOp(Grid<Real>& laplace, const Grid<Real>& grid) : KernelBase(&laplace,1) ,laplace(laplace),grid(grid) { run(); } inline void op(int i, int j, int k, Grid<Real>& laplace, const Grid<Real>& grid ) { + laplace(i,j,k) = -(6.0*grid(i,j,k)-grid(i+1,j,k)-grid(i-1,j,k)-grid(i,j+1,k)-grid(i,j-1,k)-grid(i,j,k+1)-grid(i,j,k-1)); +} inline Grid<Real>& getArg0() { return laplace; } typedef Grid<Real> type0;inline const Grid<Real>& getArg1() { return grid; } typedef Grid<Real> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, laplace,grid); } Grid<Real>& laplace; const Grid<Real>& grid; }; + +//! Kernel: get component at MAC positions + struct GetShiftedComponent : public KernelBase { GetShiftedComponent(const Grid<Vec3>& grid, Grid<Real>& comp, int dim) : KernelBase(&grid,1) ,grid(grid),comp(comp),dim(dim) { run(); } inline void op(int i, int j, int k, const Grid<Vec3>& grid, Grid<Real>& comp, int dim ) { + Vec3i ishift(i,j,k); + ishift[dim]--; + comp(i,j,k) = 0.5*(grid(i,j,k)[dim] + grid(ishift)[dim]); +} inline const Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline Grid<Real>& getArg1() { return comp; } typedef Grid<Real> type1;inline int& getArg2() { return dim; } typedef int type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, grid,comp,dim); } const Grid<Vec3>& grid; Grid<Real>& comp; int dim; };; + +//! Kernel: get component (not shifted) + struct GetComponent : public KernelBase { GetComponent(const Grid<Vec3>& grid, Grid<Real>& comp, int dim) : KernelBase(&grid,0) ,grid(grid),comp(comp),dim(dim) { run(); } inline void op(int idx, const Grid<Vec3>& grid, Grid<Real>& comp, int dim ) { + comp[idx] = grid[idx][dim]; +} inline const Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline Grid<Real>& getArg1() { return comp; } typedef Grid<Real> type1;inline int& getArg2() { return dim; } typedef int type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, grid,comp,dim); } const Grid<Vec3>& grid; Grid<Real>& comp; int dim; };; + +//! Kernel: get norm of centered grid + struct GridNorm : public KernelBase { GridNorm(Grid<Real>& n, const Grid<Vec3>& grid) : KernelBase(&n,0) ,n(n),grid(grid) { run(); } inline void op(int idx, Grid<Real>& n, const Grid<Vec3>& grid ) { + n[idx] = norm(grid[idx]); +} inline Grid<Real>& getArg0() { return n; } typedef Grid<Real> type0;inline const Grid<Vec3>& getArg1() { return grid; } typedef Grid<Vec3> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, n,grid); } Grid<Real>& n; const Grid<Vec3>& grid; };; + +//! Kernel: set component (not shifted) + struct SetComponent : public KernelBase { SetComponent(Grid<Vec3>& grid, const Grid<Real>& comp, int dim) : KernelBase(&grid,0) ,grid(grid),comp(comp),dim(dim) { run(); } inline void op(int idx, Grid<Vec3>& grid, const Grid<Real>& comp, int dim ) { + grid[idx][dim] = comp[idx]; +} inline Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline const Grid<Real>& getArg1() { return comp; } typedef Grid<Real> type1;inline int& getArg2() { return dim; } typedef int type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, grid,comp,dim); } Grid<Vec3>& grid; const Grid<Real>& comp; int dim; };; + +//! Kernel: compute centered velocity field from MAC + struct GetCentered : public KernelBase { GetCentered(Grid<Vec3>& center, const MACGrid& vel) : KernelBase(¢er,1) ,center(center),vel(vel) { run(); } inline void op(int i, int j, int k, Grid<Vec3>& center, const MACGrid& vel ) { + Vec3 v = 0.5 * ( vel(i,j,k) + Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, 0. ) ); + if(vel.is3D()) v[2] += 0.5 * vel(i,j,k+1).z; + else v[2] = 0.; + center(i,j,k) = v; +} inline Grid<Vec3>& getArg0() { return center; } typedef Grid<Vec3> type0;inline const MACGrid& getArg1() { return vel; } typedef MACGrid type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, center,vel); } Grid<Vec3>& center; const MACGrid& vel; };; + +//! Kernel: compute MAC from centered velocity field + struct GetMAC : public KernelBase { GetMAC(MACGrid& vel, const Grid<Vec3>& center) : KernelBase(&vel,1) ,vel(vel),center(center) { run(); } inline void op(int i, int j, int k, MACGrid& vel, const Grid<Vec3>& center ) { + Vec3 v = 0.5*(center(i,j,k) + Vec3(center(i-1,j,k).x, center(i,j-1,k).y, 0. )); + if(vel.is3D()) v[2] += 0.5 * center(i,j,k-1).z; + else v[2] = 0.; + vel(i,j,k) = v; +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline const Grid<Vec3>& getArg1() { return center; } typedef Grid<Vec3> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,center); } MACGrid& vel; const Grid<Vec3>& center; };; + +//! Fill in the domain boundary cells (i,j,k=0/size-1) from the neighboring cells + struct FillInBoundary : public KernelBase { FillInBoundary(Grid<Vec3>& grid, int g) : KernelBase(&grid,0) ,grid(grid),g(g) { run(); } inline void op(int i, int j, int k, Grid<Vec3>& grid, int g ) { + if (i==0) grid(i,j,k) = grid(i+1,j,k); + if (j==0) grid(i,j,k) = grid(i,j+1,k); + if (k==0) grid(i,j,k) = grid(i,j,k+1); + if (i==grid.getSizeX()-1) grid(i,j,k) = grid(i-1,j,k); + if (j==grid.getSizeY()-1) grid(i,j,k) = grid(i,j-1,k); + if (k==grid.getSizeZ()-1) grid(i,j,k) = grid(i,j,k-1); +} inline Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline int& getArg1() { return g; } typedef int type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,g); } Grid<Vec3>& grid; int g; }; + +} // namespace +#endif + diff --git a/source/blender/python/manta_pp/commonkernels.h.reg b/source/blender/python/manta_pp/commonkernels.h.reg new file mode 100644 index 00000000000..2609f03504f --- /dev/null +++ b/source/blender/python/manta_pp/commonkernels.h.reg @@ -0,0 +1 @@ +#include "commonkernels.h" diff --git a/source/blender/python/manta_pp/commonkernels.h.reg.cpp b/source/blender/python/manta_pp/commonkernels.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/commonkernels.h.reg.cpp diff --git a/source/blender/python/manta_pp/conjugategrad.cpp b/source/blender/python/manta_pp/conjugategrad.cpp new file mode 100644 index 00000000000..95caa18b63c --- /dev/null +++ b/source/blender/python/manta_pp/conjugategrad.cpp @@ -0,0 +1,316 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Conjugate gradient solver + * + ******************************************************************************/ + +#include "conjugategrad.h" +#include "commonkernels.h" + +using namespace std; +namespace Manta { + +const int CG_DEBUGLEVEL = 4; + +//***************************************************************************** +// Precondition helpers + +//! Preconditioning a la Wavelet Turbulence (needs 4 add. grids) +void InitPreconditionIncompCholesky(FlagGrid& flags, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, + Grid<Real>& orgA0, Grid<Real>& orgAi, Grid<Real>& orgAj, Grid<Real>& orgAk) +{ + // compute IC according to Golub and Van Loan + A0.copyFrom( orgA0 ); + Ai.copyFrom( orgAi ); + Aj.copyFrom( orgAj ); + Ak.copyFrom( orgAk ); + + FOR_IJK(A0) { + if (flags.isFluid(i,j,k)) { + const int idx = A0.index(i,j,k); + A0[idx] = sqrt(A0[idx]); + + // correct left and top stencil in other entries + // for i = k+1:n + // if (A(i,k) != 0) + // A(i,k) = A(i,k) / A(k,k) + Real invDiagonal = 1.0f / A0[idx]; + Ai[idx] *= invDiagonal; + Aj[idx] *= invDiagonal; + Ak[idx] *= invDiagonal; + + // correct the right and bottom stencil in other entries + // for j = k+1:n + // for i = j:n + // if (A(i,j) != 0) + // A(i,j) = A(i,j) - A(i,k) * A(j,k) + A0(i+1,j,k) -= square(Ai[idx]); + A0(i,j+1,k) -= square(Aj[idx]); + A0(i,j,k+1) -= square(Ak[idx]); + } + } + + // invert A0 for faster computation later + InvertCheckFluid (flags, A0); +}; + +//! Preconditioning using modified IC ala Bridson (needs 1 add. grid) +void InitPreconditionModifiedIncompCholesky2(FlagGrid& flags, + Grid<Real>&Aprecond, + Grid<Real>&A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + // compute IC according to Golub and Van Loan + Aprecond.clear(); + + FOR_IJK(flags) { + if (!flags.isFluid(i,j,k)) continue; + + const Real tau = 0.97; + const Real sigma = 0.25; + + // compute modified incomplete cholesky + Real e = 0.; + e = A0(i,j,k) + - square(Ai(i-1,j,k) * Aprecond(i-1,j,k) ) + - square(Aj(i,j-1,k) * Aprecond(i,j-1,k) ) + - square(Ak(i,j,k-1) * Aprecond(i,j,k-1) ) ; + e -= tau * ( + Ai(i-1,j,k) * ( Aj(i-1,j,k) + Ak(i-1,j,k) )* square( Aprecond(i-1,j,k) ) + + Aj(i,j-1,k) * ( Ai(i,j-1,k) + Ak(i,j-1,k) )* square( Aprecond(i,j-1,k) ) + + Ak(i,j,k-1) * ( Ai(i,j,k-1) + Aj(i,j,k-1) )* square( Aprecond(i,j,k-1) ) + + 0. ); + + // stability cutoff + if(e < sigma * A0(i,j,k)) + e = A0(i,j,k); + + Aprecond(i,j,k) = 1. / sqrt( e ); + } +}; + +//! Apply WT-style ICP +void ApplyPreconditionIncompCholesky(Grid<Real>& dst, Grid<Real>& Var1, FlagGrid& flags, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, + Grid<Real>& orgA0, Grid<Real>& orgAi, Grid<Real>& orgAj, Grid<Real>& orgAk) +{ + + // forward substitution + FOR_IJK(dst) { + if (!flags.isFluid(i,j,k)) continue; + dst(i,j,k) = A0(i,j,k) * (Var1(i,j,k) + - dst(i-1,j,k) * Ai(i-1,j,k) + - dst(i,j-1,k) * Aj(i,j-1,k) + - dst(i,j,k-1) * Ak(i,j,k-1)); + } + + // backward substitution + FOR_IJK_REVERSE(dst) { + const int idx = A0.index(i,j,k); + if (!flags.isFluid(idx)) continue; + dst[idx] = A0[idx] * ( dst[idx] + - dst(i+1,j,k) * Ai[idx] + - dst(i,j+1,k) * Aj[idx] + - dst(i,j,k+1) * Ak[idx]); + } +} + +//! Apply Bridson-style mICP +void ApplyPreconditionModifiedIncompCholesky2(Grid<Real>& dst, Grid<Real>& Var1, FlagGrid& flags, + Grid<Real>& Aprecond, + Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) +{ + // forward substitution + FOR_IJK(dst) { + if (!flags.isFluid(i,j,k)) continue; + const Real p = Aprecond(i,j,k); + dst(i,j,k) = p * (Var1(i,j,k) + - dst(i-1,j,k) * Ai(i-1,j,k) * Aprecond(i-1,j,k) + - dst(i,j-1,k) * Aj(i,j-1,k) * Aprecond(i,j-1,k) + - dst(i,j,k-1) * Ak(i,j,k-1) * Aprecond(i,j,k-1) ); + } + + // backward substitution + FOR_IJK_REVERSE(dst) { + const int idx = A0.index(i,j,k); + if (!flags.isFluid(idx)) continue; + const Real p = Aprecond[idx]; + dst[idx] = p * ( dst[idx] + - dst(i+1,j,k) * Ai[idx] * p + - dst(i,j+1,k) * Aj[idx] * p + - dst(i,j,k+1) * Ak[idx] * p); + } +} + + +//***************************************************************************** +// Kernels + +//! Kernel: Compute the dot product between two Real grids +/*! Uses double precision internally */ + + struct GridDotProduct : public KernelBase { GridDotProduct(const Grid<Real>& a, const Grid<Real>& b) : KernelBase(&a,0) ,a(a),b(b) ,result(0.0) { run(); } inline void op(int idx, const Grid<Real>& a, const Grid<Real>& b ,double& result) { + result += (a[idx] * b[idx]); +} inline operator double () { return result; } inline double & getRet() { return result; } inline const Grid<Real>& getArg0() { return a; } typedef Grid<Real> type0;inline const Grid<Real>& getArg1() { return b; } typedef Grid<Real> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, a,b,result); } const Grid<Real>& a; const Grid<Real>& b; double result; };; + +//! Kernel: compute residual (init) and add to sigma + + + struct InitSigma : public KernelBase { InitSigma(FlagGrid& flags, Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& temp) : KernelBase(&flags,0) ,flags(flags),dst(dst),rhs(rhs),temp(temp) ,sigma(0) { run(); } inline void op(int idx, FlagGrid& flags, Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& temp ,double& sigma) { + const double res = rhs[idx] - temp[idx]; + dst[idx] = (Real)res; + + // only compute residual in fluid region + if(flags.isFluid(idx)) + sigma += res*res; +} inline operator double () { return sigma; } inline double & getRet() { return sigma; } inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return dst; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return rhs; } typedef Grid<Real> type2;inline Grid<Real>& getArg3() { return temp; } typedef Grid<Real> type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, flags,dst,rhs,temp,sigma); } FlagGrid& flags; Grid<Real>& dst; Grid<Real>& rhs; Grid<Real>& temp; double sigma; };; + +//! Kernel: update search vector + + struct UpdateSearchVec : public KernelBase { UpdateSearchVec(Grid<Real>& dst, Grid<Real>& src, Real factor) : KernelBase(&dst,0) ,dst(dst),src(src),factor(factor) { run(); } inline void op(int idx, Grid<Real>& dst, Grid<Real>& src, Real factor ) { + dst[idx] = src[idx] + factor * dst[idx]; +} inline Grid<Real>& getArg0() { return dst; } typedef Grid<Real> type0;inline Grid<Real>& getArg1() { return src; } typedef Grid<Real> type1;inline Real& getArg2() { return factor; } typedef Real type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, dst,src,factor); } Grid<Real>& dst; Grid<Real>& src; Real factor; }; + +//***************************************************************************** +// CG class + +template<class APPLYMAT> +GridCg<APPLYMAT>::GridCg(Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& residual, Grid<Real>& search, FlagGrid& flags, Grid<Real>& tmp, + Grid<Real>* pA0, Grid<Real>* pAi, Grid<Real>* pAj, Grid<Real>* pAk) : + GridCgInterface(), mInited(false), mIterations(0), mDst(dst), mRhs(rhs), mResidual(residual), + mSearch(search), mFlags(flags), mTmp(tmp), mpA0(pA0), mpAi(pAi), mpAj(pAj), mpAk(pAk), + mPcMethod(PC_None), mpPCA0(pA0), mpPCAi(pAi), mpPCAj(pAj), mpPCAk(pAk), mSigma(0.), mAccuracy(VECTOR_EPSILON), mResNorm(1e20) +{ + dst.clear(); + residual.clear(); + search.clear(); + tmp.clear(); +} + +template<class APPLYMAT> +void GridCg<APPLYMAT>::doInit() { + mInited = true; + + mResidual.copyFrom( mRhs ); // p=0, residual = b + + if (mPcMethod == PC_ICP) { + assertMsg(mDst.is3D(), "ICP only supports 3D grids so far"); + InitPreconditionIncompCholesky(mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + ApplyPreconditionIncompCholesky(mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + } else if (mPcMethod == PC_mICP) { + assertMsg(mDst.is3D(), "mICP only supports 3D grids so far"); + InitPreconditionModifiedIncompCholesky2(mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + ApplyPreconditionModifiedIncompCholesky2(mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + } else { + mTmp.copyFrom( mResidual ); + } + + mSearch.copyFrom( mTmp ); + + mSigma = GridDotProduct(mTmp, mResidual); +} + +template<class APPLYMAT> +bool GridCg<APPLYMAT>::iterate() { + if(!mInited) doInit(); + + mIterations++; + + // create matrix application operator passed as template argument, + // this could reinterpret the mpA pointers (not so clean right now) + // tmp = applyMat(search) + + APPLYMAT (mFlags, mTmp, mSearch, *mpA0, *mpAi, *mpAj, *mpAk); + + // alpha = sigma/dot(tmp, search) + Real dp = GridDotProduct(mTmp, mSearch); + Real alpha = 0.; + if(fabs(dp)>0.) alpha = mSigma / (Real)dp; + + gridScaledAdd<Real,Real>(mDst, mSearch, alpha); // dst += search * alpha + gridScaledAdd<Real,Real>(mResidual, mTmp, -alpha); // residual += tmp * -alpha + + if (mPcMethod == PC_ICP) + ApplyPreconditionIncompCholesky(mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + else if (mPcMethod == PC_mICP) + ApplyPreconditionModifiedIncompCholesky2(mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + else + mTmp.copyFrom( mResidual ); + + // compute norm of the residual? + if(this->mUseResNorm) { + mResNorm = GridSumSqr(mResidual).sum; + } else { + mResNorm = mResidual.getMaxAbsValue(); + } + //if(mIterations % 10 == 9) debMsg("GridCg::Iteration i="<<mIterations<<", resNorm="<<mResNorm<<" accuracy="<<mAccuracy, 1); + + // abort here to safe some work... + if(mResNorm<mAccuracy) { + mSigma = mResNorm; // this will be returned later on to the caller... + return false; + } + + Real sigmaNew = GridDotProduct(mTmp, mResidual); + Real beta = sigmaNew / mSigma; + + // search = tmp + beta * search + UpdateSearchVec (mSearch, mTmp, beta); + + debMsg("PB-Cg::iter i="<<mIterations<<" sigmaNew="<<sigmaNew<<" sigmaLast="<<mSigma<<" alpha="<<alpha<<" beta="<<beta<<" ", CG_DEBUGLEVEL); + mSigma = sigmaNew; + + //debMsg("PB-CG-Norms::p"<<sqrt( GridOpNormNosqrt(mpDst, mpFlags).getValue() ) <<" search"<<sqrt( GridOpNormNosqrt(mpSearch, mpFlags).getValue(), CG_DEBUGLEVEL ) <<" res"<<sqrt( GridOpNormNosqrt(mpResidual, mpFlags).getValue() ) <<" tmp"<<sqrt( GridOpNormNosqrt(mpTmp, mpFlags).getValue() ), CG_DEBUGLEVEL ); // debug + return true; +} + +template<class APPLYMAT> +void GridCg<APPLYMAT>::solve(int maxIter) { + for (int iter=0; iter<maxIter; iter++) { + if (!iterate()) iter=maxIter; + } + return; +} + +static bool gPrint2dWarning = true; +template<class APPLYMAT> +void GridCg<APPLYMAT>::setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak) { + mPcMethod = method; + if( (!A0->is3D()) && (mPcMethod!=PC_None) ) { + if(gPrint2dWarning) { + debMsg("Pre-conditioning only supported in 3D for now, disabling it.", 1); + gPrint2dWarning = false; + } + mPcMethod=PC_None; + } + mpPCA0 = A0; + mpPCAi = Ai; + mpPCAj = Aj; + mpPCAk = Ak; +} + +// explicit instantiation +template class GridCg<ApplyMatrix>; +template class GridCg<ApplyMatrix2D>; + +}; // DDF + + diff --git a/source/blender/python/manta_pp/conjugategrad.h b/source/blender/python/manta_pp/conjugategrad.h new file mode 100644 index 00000000000..ccf5983e808 --- /dev/null +++ b/source/blender/python/manta_pp/conjugategrad.h @@ -0,0 +1,181 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Conjugate gradient solver + * + ******************************************************************************/ + +#ifndef _CONJUGATEGRADIENT_H +#define _CONJUGATEGRADIENT_H + +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" + +namespace Manta { + +static const bool CG_DEBUG = false; + +//! Basic CG interface +class GridCgInterface { + public: + enum PreconditionType { PC_None=0, PC_ICP, PC_mICP }; + + GridCgInterface() : mUseResNorm(true) {}; + virtual ~GridCgInterface() {}; + + // solving functions + virtual bool iterate() = 0; + virtual void solve(int maxIter) = 0; + + // precond + virtual void setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak) = 0; + + // access + virtual Real getSigma() const = 0; + virtual Real getIterations() const = 0; + virtual Real getResNorm() const = 0; + virtual void setAccuracy(Real set) = 0; + virtual Real getAccuracy() const = 0; + + void setUseResNorm(bool set) { mUseResNorm = set; } + + protected: + + // use norm of residual, or max value for threshold? + bool mUseResNorm; +}; + + +//! Run single iteration of the cg solver +/*! the template argument determines the type of matrix multiplication, + typically a ApplyMatrix kernel, another one is needed e.g. for the + mesh-based wave equation solver */ +template<class APPLYMAT> +class GridCg : public GridCgInterface { + public: + //! constructor + GridCg(Grid<Real>& dst, Grid<Real>& rhs, Grid<Real>& residual, Grid<Real>& search, FlagGrid& flags, Grid<Real>& tmp, + Grid<Real>* A0, Grid<Real>* pAi, Grid<Real>* pAj, Grid<Real>* pAk); + ~GridCg() {} + + void doInit(); + bool iterate(); + void solve(int maxIter); + //! init pointers, and copy values from "normal" matrix + void setPreconditioner(PreconditionType method, Grid<Real> *A0, Grid<Real> *Ai, Grid<Real> *Aj, Grid<Real> *Ak); + + // Accessors + Real getSigma() const { return mSigma; } + Real getIterations() const { return mIterations; } + + Real getResNorm() const { return mResNorm; } + + void setAccuracy(Real set) { mAccuracy=set; } + Real getAccuracy() const { return mAccuracy; } + + protected: + bool mInited; + int mIterations; + // grids + Grid<Real>& mDst; + Grid<Real>& mRhs; + Grid<Real>& mResidual; + Grid<Real>& mSearch; + FlagGrid& mFlags; + Grid<Real>& mTmp; + + Grid<Real> *mpA0, *mpAi, *mpAj, *mpAk; + + PreconditionType mPcMethod; + //! preconditioning grids + Grid<Real> *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk; + + //! sigma / residual + Real mSigma; + //! accuracy of solver (max. residuum) + Real mAccuracy; + //! norm of the residual + Real mResNorm; +}; // GridCg + + +//! Kernel: Apply symmetric stored Matrix + + + + struct ApplyMatrix : public KernelBase { ApplyMatrix(FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) : KernelBase(&flags,0) ,flags(flags),dst(dst),src(src),A0(A0),Ai(Ai),Aj(Aj),Ak(Ak) { run(); } inline void op(int idx, FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak ) { + if (!flags.isFluid(idx)) { + dst[idx] = src[idx]; + return; + } + dst[idx] = src[idx] * A0[idx] + + src[idx-X] * Ai[idx-X] + + src[idx+X] * Ai[idx] + + src[idx-Y] * Aj[idx-Y] + + src[idx+Y] * Aj[idx] + + src[idx-Z] * Ak[idx-Z] + + src[idx+Z] * Ak[idx]; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return dst; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return src; } typedef Grid<Real> type2;inline Grid<Real>& getArg3() { return A0; } typedef Grid<Real> type3;inline Grid<Real>& getArg4() { return Ai; } typedef Grid<Real> type4;inline Grid<Real>& getArg5() { return Aj; } typedef Grid<Real> type5;inline Grid<Real>& getArg6() { return Ak; } typedef Grid<Real> type6; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, flags,dst,src,A0,Ai,Aj,Ak); } FlagGrid& flags; Grid<Real>& dst; Grid<Real>& src; Grid<Real>& A0; Grid<Real>& Ai; Grid<Real>& Aj; Grid<Real>& Ak; }; + +//! Kernel: Apply symmetric stored Matrix. 2D version + + + + struct ApplyMatrix2D : public KernelBase { ApplyMatrix2D(FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) : KernelBase(&flags,0) ,flags(flags),dst(dst),src(src),A0(A0),Ai(Ai),Aj(Aj),Ak(Ak) { run(); } inline void op(int idx, FlagGrid& flags, Grid<Real>& dst, Grid<Real>& src, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak ) { + unusedParameter(Ak); // only there for parameter compatibility with ApplyMatrix + + if (!flags.isFluid(idx)) { + dst[idx] = src[idx]; + return; + } + dst[idx] = src[idx] * A0[idx] + + src[idx-X] * Ai[idx-X] + + src[idx+X] * Ai[idx] + + src[idx-Y] * Aj[idx-Y] + + src[idx+Y] * Aj[idx]; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return dst; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return src; } typedef Grid<Real> type2;inline Grid<Real>& getArg3() { return A0; } typedef Grid<Real> type3;inline Grid<Real>& getArg4() { return Ai; } typedef Grid<Real> type4;inline Grid<Real>& getArg5() { return Aj; } typedef Grid<Real> type5;inline Grid<Real>& getArg6() { return Ak; } typedef Grid<Real> type6; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, flags,dst,src,A0,Ai,Aj,Ak); } FlagGrid& flags; Grid<Real>& dst; Grid<Real>& src; Grid<Real>& A0; Grid<Real>& Ai; Grid<Real>& Aj; Grid<Real>& Ak; }; + +//! Kernel: Construct the matrix for the poisson equation + + struct MakeLaplaceMatrix : public KernelBase { MakeLaplaceMatrix(FlagGrid& flags, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak) : KernelBase(&flags,1) ,flags(flags),A0(A0),Ai(Ai),Aj(Aj),Ak(Ak) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak ) { + if (!flags.isFluid(i,j,k)) + return; + + // center + if (!flags.isObstacle(i-1,j,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i+1,j,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i,j-1,k)) A0(i,j,k) += 1.; + if (!flags.isObstacle(i,j+1,k)) A0(i,j,k) += 1.; + if (flags.is3D() && !flags.isObstacle(i,j,k-1)) A0(i,j,k) += 1.; + if (flags.is3D() && !flags.isObstacle(i,j,k+1)) A0(i,j,k) += 1.; + + if (flags.isFluid(i+1,j,k)) Ai(i,j,k) = -1.; + if (flags.isFluid(i,j+1,k)) Aj(i,j,k) = -1.; + if (flags.is3D() && flags.isFluid(i,j,k+1)) Ak(i,j,k) = -1.; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return A0; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return Ai; } typedef Grid<Real> type2;inline Grid<Real>& getArg3() { return Aj; } typedef Grid<Real> type3;inline Grid<Real>& getArg4() { return Ak; } typedef Grid<Real> type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,A0,Ai,Aj,Ak); } FlagGrid& flags; Grid<Real>& A0; Grid<Real>& Ai; Grid<Real>& Aj; Grid<Real>& Ak; }; + + + + +} // namespace + +#endif + diff --git a/source/blender/python/manta_pp/conjugategrad.h.reg b/source/blender/python/manta_pp/conjugategrad.h.reg new file mode 100644 index 00000000000..90e55a88da4 --- /dev/null +++ b/source/blender/python/manta_pp/conjugategrad.h.reg @@ -0,0 +1 @@ +#include "conjugategrad.h" diff --git a/source/blender/python/manta_pp/conjugategrad.h.reg.cpp b/source/blender/python/manta_pp/conjugategrad.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/conjugategrad.h.reg.cpp diff --git a/source/blender/python/manta_pp/edgecollapse.cpp b/source/blender/python/manta_pp/edgecollapse.cpp new file mode 100644 index 00000000000..683349b5343 --- /dev/null +++ b/source/blender/python/manta_pp/edgecollapse.cpp @@ -0,0 +1,678 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Mesh edge collapse and subdivision + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#include "edgecollapse.h" +#include <queue> + +using namespace std; + +namespace Manta { + +// 8-point butterfly subdivision scheme (as described by Brochu&Bridson 2009) +Vec3 ButterflySubdivision(Mesh& m, const Corner &ca, const Corner &cb) +{ + Vec3 p = m.nodes(m.corners(ca.prev).node).pos + m.nodes(m.corners(ca.next).node).pos; + Vec3 q = m.nodes(ca.node).pos + m.nodes(cb.node).pos; + Vec3 r = m.nodes(m.corners(m.corners(ca.next).opposite).node).pos + + m.nodes(m.corners(m.corners(ca.prev).opposite).node).pos + + m.nodes(m.corners(m.corners(cb.next).opposite).node).pos + + m.nodes(m.corners(m.corners(cb.prev).opposite).node).pos; + return ( 8*p + 2*q - r)/16.0; +} + +// Modified Butterfly Subdivision Scheme from: +// Interpolating Subdivision for Meshes with Arbitrary Topology +// Denis Zorin, Peter Schroder, and Wim Sweldens +// input the Corner that satisfies the following: +// c.prev.node is the extraordinary vertex, +// and c.next.node is the other vertex involved in the subdivision +Vec3 OneSidedButterflySubdivision(Mesh& m, const int valence, const Corner &c) { + Vec3 out; + Vec3 p0 = m.nodes(m.corners(c.prev).node).pos; + Vec3 p1 = m.nodes(m.corners(c.next).node).pos; + + if(valence==3) { + Vec3 p2 = m.nodes(c.node).pos; + Vec3 p3 = m.nodes(m.corners(m.corners(c.next).opposite).node).pos; + out = (5.0/12.0)*p1 - (1.0/12.0)*(p2+p3) + 0.75*p0; + } + else if(valence==4) { + Vec3 p2 = m.nodes(m.corners(m.corners(c.next).opposite).node).pos; + out = 0.375*p1 - 0.125*p2 + 0.75*p0; + } + else { + // rotate around extraordinary vertex, + // calculate subdivision weights, + // and interpolate vertex position + double rv = 1.0/(double)valence; + out = 0.0; + int current = c.prev; + for(int j=0; j<valence; j++) { + double s = (0.25 + cos(2*M_PI*j*rv) + 0.5*cos(4*M_PI*j*rv))*rv; + Vec3 p = m.nodes(m.corners(m.corners(current).prev).node).pos; + + out += s*p; + current = m.corners(m.corners(m.corners(current).next).opposite).next; + } + out += 0.75* m.nodes(m.corners(c.prev).node).pos; + } + return out; +} + +// Modified Butterfly Subdivision Scheme from: +// Interpolating Subdivision for Meshes with Arbitrary Topology +// Denis Zorin, Peter Schroder, and Wim Sweldens +Vec3 ModifiedButterflySubdivision(Mesh& m, const Corner &ca, const Corner &cb, const Vec3& fallback) +{ + // calculate the valence of the two parent vertices + int start = ca.prev; + int current = start; + int valenceA = 0; + do { + valenceA++; + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) return fallback; + current = m.corners(op).next; + } + while(current != start); + start = ca.next; + current = start; + int valenceB = 0; + do { + valenceB++; + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) return fallback; + current = m.corners(op).next; + } + while(current != start); + + // if both vertices have valence 6, use butterfly subdivision + if(valenceA==6 && valenceB==6) { + return ButterflySubdivision(m, ca,cb); + } + else if(valenceA==6) // use a one-sided scheme + { + return OneSidedButterflySubdivision(m, valenceB,cb); + } + else if(valenceB==6) // use a one-sided scheme + { + return OneSidedButterflySubdivision(m, valenceA,ca); + } + else // average the results from two one-sided schemes + { + return 0.5*( OneSidedButterflySubdivision(m, valenceA,ca) + + OneSidedButterflySubdivision(m, valenceB,cb) ); + } +} + +bool gAbort = false; + +// collapse an edge on triangle "trinum". +// "which" is 0,1, or 2, +// where which==0 is the triangle edge from p0 to p1, +// which==1 is the triangle edge from p1 to p2, +// and which==2 is the triangle edge from p2 to p0, +void CollapseEdge(Mesh& m, const int trinum, const int which, const Vec3 &edgevect, const Vec3 &endpoint, + vector<int> &deletedNodes, std::map<int,bool> &taintedTris, int &numCollapses, bool doTubeCutting) +{ + if (gAbort) return; + // I wanted to draw a pretty picture of an edge collapse, + // but I don't know how to make wacky angled lines in ASCII. + // Instead, I will show the before case and tell you what needs to be done. + + // BEFORE: + // * + // / \. + // /C0 \. + // / \. + // / \. + // / B \. + // / \. + // /C1 C2 \. + // P0 *---------------* P1 + // \C2 C1 / + // \ / + // \ A / + // \ / + // \ / + // \C0 / + // \ / + // * + // + // We are going to collapse the edge between P0 and P1 + // by deleting P1, + // and taking all references to P1, + // and rerouting them to P0 instead + // + // What we need to do: + // Move position of P0 + // Preserve connectivity in both triangles: + // (C1.opposite).opposite = C2.o + // (C2.opposite).opposite = C1.o + // Delete references to Corners of deleted triangles in both P0 and P1's Corner list + // Reassign references to P1: + // loop through P1 triangles: + // rename P1 references to P0 in p lists. + // rename Corner.v references + // Copy P1's list of Corners over to P0's list of Corners + // Delete P1 + + Corner ca_old[3], cb_old[3]; + ca_old[0] = m.corners(trinum, which); + ca_old[1] = m.corners(ca_old[0].next); + ca_old[2] = m.corners(ca_old[0].prev); + bool haveB = false; + if (ca_old[0].opposite>=0) { + cb_old[0] = m.corners(ca_old[0].opposite); + cb_old[1] = m.corners(cb_old[0].next); + cb_old[2] = m.corners(cb_old[0].prev); + haveB = true; + } + if (!haveB) { + // for now, don't collapse + return; + } + + int P0 = ca_old[2].node; + int P1 = ca_old[1].node; + + /////////////// + // avoid creating nonmanifold edges + bool nonmanifold = false; + bool nonmanifold2 = false; + + set<int>& ring0 = m.get1Ring(P0).nodes; + set<int>& ring1 = m.get1Ring(P1).nodes; + + // check for intersections of the 1-rings of P0,P1 + int cl=0, commonVert=-1; + for(set<int>::iterator it=ring1.begin(); it != ring1.end(); ++it) + if (ring0.find(*it) != ring0.end()) { + cl++; + if (*it != ca_old[0].node && *it != cb_old[0].node) commonVert = *it; + } + + nonmanifold = cl>2; + nonmanifold2 = cl>3; + + if(nonmanifold && + ca_old[1].opposite>=0 && cb_old[1].opposite>=0 && + ca_old[2].opposite>=0 && cb_old[2].opposite>=0 ) // collapsing this edge would create a non-manifold edge + { + if(nonmanifold2) + return; + + bool topTet = false; + bool botTet = false; + // check if collapsing this edge will collapse a tet. + if(m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node) + botTet = true; + + if(m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node) + topTet = true; + + if(topTet^botTet) { + + // safe pyramid case. + // collapse the whole tet! + // First collapse the top of the pyramid, + // then carry on collapsing the original verts. + Corner cc_old[3],cd_old[3]; + if(botTet) + cc_old[0] = m.corners(ca_old[1].opposite); + else // topTet + cc_old[0] = cb_old[2]; + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + for (int i=0; i<m.numTriChannels(); i++) + {};//TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + m.removeTriFromLookup(tmpTrinum); + m.removeTriFromLookup(tmpOthertri); + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + deletedNodes.push_back(P3); + + numCollapses++; + + // recompute Corners for triangles A and B + if(botTet) + ca_old[0] = m.corners(ca_old[2].opposite); + else + ca_old[0] = m.corners(ca_old[1].prev); + ca_old[1] = m.corners(ca_old[0].next); + ca_old[2] = m.corners(ca_old[0].prev); + cb_old[0] = m.corners(ca_old[0].opposite); + cb_old[1] = m.corners(cb_old[0].next); + cb_old[2] = m.corners(cb_old[0].prev); + + /////////////// + // avoid creating nonmanifold edges... again + ring0 = m.get1Ring(ca_old[2].node).nodes; + ring1 = m.get1Ring(ca_old[1].node).nodes; + + // check for intersections of the 1-rings of P0,P1 + cl=0; + for(set<int>::iterator it=ring1.begin(); it != ring1.end(); ++it) + if (*it != ca_old[0].node && ring0.find(*it) != ring0.end()) + cl++; + + if(cl>2) { // nonmanifold + // this can happen if collapsing the first tet leads to another similar collapse that requires the collapse of a tet. + // for now, just move on and pick this up later. + + // if the original component was very small, this first collapse could have led to a tiny piece of nonmanifold geometry. + // in this case, just delete everything that remains. + if(m.corners(ca_old[0].opposite).tri==cb_old[0].tri && m.corners(ca_old[1].opposite).tri==cb_old[0].tri && m.corners(ca_old[2].opposite).tri==cb_old[0].tri) { + taintedTris[ca_old[0].tri] = true; + taintedTris[cb_old[0].tri] = true; + m.removeTriFromLookup(ca_old[0].tri); + m.removeTriFromLookup(cb_old[0].tri); + deletedNodes.push_back(ca_old[0].node); + deletedNodes.push_back(ca_old[1].node); + deletedNodes.push_back(ca_old[2].node); + } + return; + } + } else if(topTet && botTet && ca_old[1].opposite>=0 && ca_old[2].opposite>=0 && cb_old[1].opposite>=0 && cb_old[2].opposite>=0) + { + if(!(m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node && + m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node && + (m.corners(ca_old[1].opposite).node == m.corners(cb_old[1].opposite).node || + (m.corners(ca_old[1].opposite).node == cb_old[0].node && + m.corners(cb_old[1].opposite).node == ca_old[0].node) ))) + { + // just collapse one for now. + + // collapse the whole tet! + // First collapse the top of the pyramid, + // then carry on collapsing the original verts. + Corner cc_old[3],cd_old[3]; + + // collapse top + { + cc_old[0] = m.corners(ca_old[1].opposite); + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + m.removeTriFromLookup(tmpTrinum); + m.removeTriFromLookup(tmpOthertri); + deletedNodes.push_back(P3); + + numCollapses++; + } + // then collapse bottom + { + //cc_old[0] = [ca_old[1].opposite; + cc_old[0] = cb_old[2]; + cc_old[1] = m.corners(cc_old[0].next); + cc_old[2] = m.corners(cc_old[0].prev); + if (cc_old[0].opposite<0) return; + cd_old[0] = m.corners(cc_old[0].opposite); + cd_old[1] = m.corners(cd_old[0].next); + cd_old[2] = m.corners(cd_old[0].prev); + int P2 = cc_old[2].node; + int P3 = cc_old[1].node; + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P2,P3, cc_old[0], cd_old[0]); + + m.mergeNode(P2, P3); + + // Preserve connectivity in both triangles + if (cc_old[1].opposite>=0) + m.corners(cc_old[1].opposite).opposite = cc_old[2].opposite; + if (cc_old[2].opposite>=0) + m.corners(cc_old[2].opposite).opposite = cc_old[1].opposite; + if (cd_old[1].opposite>=0) + m.corners(cd_old[1].opposite).opposite = cd_old[2].opposite; + if (cd_old[2].opposite>=0) + m.corners(cd_old[2].opposite).opposite = cd_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + int tmpTrinum = cc_old[0].tri; + int tmpOthertri = cd_old[0].tri; + taintedTris[tmpTrinum] = true; + taintedTris[tmpOthertri] = true; + deletedNodes.push_back(P3); + + numCollapses++; + } + + // Though we've collapsed a lot of stuff, we still haven't collapsed the original edge. + // At this point we still haven't guaranteed that this original collapse weill be safe. + // quit for now, and we'll catch the remaining short edges the next time this function is called. + return; + } + } + else if (doTubeCutting) + { + // tube case + //cout<<"CollapseEdge:tube case" << endl; + + // find the edges that touch the common vert + int P2 = commonVert; + int P1P2=-1, P2P1, P2P0=-1, P0P2=-1; // corners across from the cutting seam + int start = ca_old[0].next; + int end = cb_old[0].prev; + int current = start; + do { + // rotate around vertex P1 counter-clockwise + int op = m.corners(m.corners(current).next).opposite; + if (op < 0) throw Error("tube cutting failed, no opposite"); + current = m.corners(op).next; + + if(m.corners(m.corners(current).prev).node==commonVert) + P1P2 = m.corners(current).next; + } + while(current != end); + + start = ca_old[0].prev; + end = cb_old[0].next; + current = start; + do { + // rotate around vertex P0 clockwise + int op = m.corners(m.corners(current).prev).opposite; + if (op < 0) throw Error("tube cutting failed, no opposite"); + + current = m.corners(op).prev; + if(m.corners(m.corners(current).next).node==commonVert) + P2P0 = m.corners(current).prev; + } while(current != end); + + if (P1P2 < 0 || P2P0 < 0) + throw Error("tube cutting failed, ill geometry"); + + P2P1 = m.corners(P1P2).opposite; + P0P2 = m.corners(P2P0).opposite; + + // duplicate vertices on the top half of the cut, + // and use them to split the tube at this seam + int P0b = m.addNode(Node(m.nodes(P0).pos)); + int P1b = m.addNode(Node(m.nodes(P1).pos)); + int P2b = m.addNode(Node(m.nodes(P2).pos)); + for (int i=0; i<m.numNodeChannels(); i++) { + m.nodeChannel(i)->addInterpol(P0, P0, 0.5); + m.nodeChannel(i)->addInterpol(P1, P1, 0.5); + m.nodeChannel(i)->addInterpol(P2, P2, 0.5); + } + + // offset the verts in the normal directions to avoid self intersections + Vec3 offsetVec = cross(m.nodes(P1).pos-m.nodes(P0).pos, m.nodes(P2).pos-m.nodes(P0).pos); + normalize(offsetVec); + offsetVec *= 0.01; // HACK: + m.nodes(P0).pos -= offsetVec; + m.nodes(P1).pos -= offsetVec; + m.nodes(P2).pos -= offsetVec; + m.nodes(P0b).pos += offsetVec; + m.nodes(P1b).pos += offsetVec; + m.nodes(P2b).pos += offsetVec; + + // create a list of all triangles which touch P0, P1, and P2 from the top, + map<int,bool> topTris; + start = cb_old[0].next; + end = m.corners(P0P2).prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + start = m.corners(P0P2).next; + end = m.corners(P2P1).prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + start = m.corners(P2P1).next; + end = cb_old[0].prev; + current = start; + topTris[start/3]=true; + do { + // rotate around vertex P0 counter-clockwise + current = m.corners(m.corners(m.corners(current).next).opposite).next; + topTris[current/3]=true; + } while(current != end); + + // create two new triangles, + int Ta = m.addTri(Triangle(P0,P1,P2)); + int Tb = m.addTri(Triangle(P1b,P0b,P2b)); + for (int i=0; i<m.numTriChannels(); i++) { + m.triChannel(i)->addNew(); + m.triChannel(i)->addNew(); + } + + // sew the tris to close the cut on each side + for(int c=0; c<3; c++) m.addCorner(Corner(Ta, m.tris(Ta).c[c])); + for(int c=0; c<3; c++) m.addCorner(Corner(Tb, m.tris(Tb).c[c])); + for(int c=0; c<3; c++) { + m.corners(Ta,c).next = 3*Ta+((c+1)%3); + m.corners(Ta,c).prev = 3*Ta+((c+2)%3); + m.corners(Tb,c).next = 3*Tb+((c+1)%3); + m.corners(Tb,c).prev = 3*Tb+((c+2)%3); + } + m.corners(Ta,0).opposite = P1P2; + m.corners(Ta,1).opposite = P2P0; + m.corners(Ta,2).opposite = ca_old[1].prev; + m.corners(Tb,0).opposite = P0P2; + m.corners(Tb,1).opposite = P2P1; + m.corners(Tb,2).opposite = cb_old[1].prev; + for (int c=0; c<3; c++) { + m.corners(m.corners(Ta,c).opposite).opposite = 3*Ta+c; + m.corners(m.corners(Tb,c).opposite).opposite = 3*Tb+c; + } + // replace P0,P1,P2 on the top with P0b,P1b,P2b. + for(map<int,bool>::iterator tti=topTris.begin(); tti!=topTris.end(); tti++) { + //cout << "H " << tti->first << " : " << m.tris(tti->first).c[0] << " " << m.tris(tti->first).c[1] << " " << m.tris(tti->first).c[2] << " " << endl; + for(int i=0; i<3; i++) { + int cn = m.tris(tti->first).c[i]; + set<int>& ring = m.get1Ring(cn).nodes; + + if (ring.find(P0) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P0); + ring.insert(P0b); + m.get1Ring(P0).nodes.erase(cn); + m.get1Ring(P0b).nodes.insert(cn); + } + if (ring.find(P1) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P1); + ring.insert(P1b); + m.get1Ring(P1).nodes.erase(cn); + m.get1Ring(P1b).nodes.insert(cn); + } + if (ring.find(P2) != ring.end() && cn!=P0 && cn!=P1 && cn!=P2 && cn!=P0b && cn!=P1b && cn!=P2b) { + ring.erase(P2); + ring.insert(P2b); + m.get1Ring(P2).nodes.erase(cn); + m.get1Ring(P2b).nodes.insert(cn); + } + if(cn==P0) { + m.tris(tti->first).c[i]=P0b; + m.corners(tti->first,i).node = P0b; + m.get1Ring(P0).tris.erase(tti->first); + m.get1Ring(P0b).tris.insert(tti->first); + } + else if(cn==P1) { + m.tris(tti->first).c[i]=P1b; + m.corners(tti->first,i).node = P1b; + m.get1Ring(P1).tris.erase(tti->first); + m.get1Ring(P1b).tris.insert(tti->first); + } + else if(cn==P2) { + m.tris(tti->first).c[i]=P2b; + m.corners(tti->first,i).node = P2b; + m.get1Ring(P2).tris.erase(tti->first); + m.get1Ring(P2b).tris.insert(tti->first); + } + } + } + + //m.sanityCheck(true, &deletedNodes, &taintedTris); + + return; + } + return; + } + if(ca_old[1].opposite>=0 && ca_old[2].opposite>=0 && cb_old[1].opposite>=0 && cb_old[2].opposite>=0 && ca_old[0].opposite>=0 && cb_old[0].opposite>=0 && + ((m.corners(ca_old[1].opposite).node == m.corners(ca_old[2].opposite).node && // two-pyramid tubey case (6 tris, 5 verts) + m.corners(cb_old[1].opposite).node == m.corners(cb_old[2].opposite).node && + (m.corners(ca_old[1].opposite).node == m.corners(cb_old[1].opposite).node || + (m.corners(ca_old[1].opposite).node==cb_old[0].node && // single tetrahedron case + m.corners(cb_old[1].opposite).node==ca_old[0].node) )) + || + (m.corners(ca_old[0].opposite).tri==m.corners(cb_old[0].opposite).tri && m.corners(ca_old[1].opposite).tri==m.corners(cb_old[0].opposite).tri && m.corners(ca_old[2].opposite).tri==m.corners(cb_old[0].opposite).tri // nonmanifold: 2 tris, 3 verts + && m.corners(cb_old[0].opposite).tri==m.corners(ca_old[0].opposite).tri && m.corners(cb_old[1].opposite).tri==m.corners(ca_old[0].opposite).tri && m.corners(cb_old[2].opposite).tri==m.corners(ca_old[0].opposite).tri) + )) + { + // both top and bottom are closed pyramid caps, or it is a single tet + // delete the whole component! + // flood fill to mark all triangles in the component + map<int,bool> markedTris; + queue<int> triQ; + triQ.push(trinum); + markedTris[trinum] = true; + int iters = 0; + while(!triQ.empty()) { + int trival = triQ.front(); + triQ.pop(); + for(int i=0; i<3; i++) { + int newtri = m.corners(m.corners(trival,i).opposite).tri; + if(markedTris.find(newtri)==markedTris.end()) { + triQ.push(newtri); + markedTris[newtri] = true; + } + } + iters++; + } + map<int,bool> markedverts; + for(map<int,bool>::iterator mit=markedTris.begin(); mit!=markedTris.end(); mit++) { + taintedTris[mit->first] = true; + markedverts[m.tris(mit->first).c[0]] = true; + markedverts[m.tris(mit->first).c[1]] = true; + markedverts[m.tris(mit->first).c[2]] = true; + } + for(map<int,bool>::iterator mit=markedverts.begin(); mit!=markedverts.end(); mit++) + deletedNodes.push_back(mit->first); + return; + } + + ////////////////////////// + // begin original edge collapse + + // update tri props of all adjacent triangles of P0,P1 (do before CT updates!) + // TODO: handleTriPropertyEdgeCollapse(trinum, P0,P1, ca_old[0], cb_old[0]); + + m.mergeNode(P0, P1); + + // Move position of P0 + m.nodes(P0).pos = endpoint + 0.5*edgevect; + + // Preserve connectivity in both triangles + if (ca_old[1].opposite>=0) + m.corners(ca_old[1].opposite).opposite = ca_old[2].opposite; + if (ca_old[2].opposite>=0) + m.corners(ca_old[2].opposite).opposite = ca_old[1].opposite; + if (haveB && cb_old[1].opposite>=0) + m.corners(cb_old[1].opposite).opposite = cb_old[2].opposite; + if (haveB && cb_old[2].opposite>=0) + m.corners(cb_old[2].opposite).opposite = cb_old[1].opposite; + + //////////////////// + // mark the two triangles and the one node for deletion + taintedTris[ca_old[0].tri] = true; + m.removeTriFromLookup(ca_old[0].tri); + if (haveB) { + taintedTris[cb_old[0].tri] = true; + m.removeTriFromLookup(cb_old[0].tri); + } + deletedNodes.push_back(P1); + numCollapses++; +} + +} // namespace + diff --git a/source/blender/python/manta_pp/edgecollapse.h b/source/blender/python/manta_pp/edgecollapse.h new file mode 100644 index 00000000000..70710a1346a --- /dev/null +++ b/source/blender/python/manta_pp/edgecollapse.h @@ -0,0 +1,48 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Mesh edge collapse and subdivision + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#ifndef _EDGECOLLAPSE_H +#define _EDGECOLLAPSE_H + +#include "mesh.h" + +namespace Manta { + +void CollapseEdge(Mesh& mesh, const int trinum, const int which, const Vec3 &edgevect, const Vec3 &endpoint, + std::vector<int> &deletedNodes, std::map<int,bool> &taintedTris, int &numCollapses, bool doTubeCutting); + +Vec3 ModifiedButterflySubdivision(Mesh& mesh, const Corner& ca, const Corner& cb, const Vec3& fallback); + +} + +#endif + diff --git a/source/blender/python/manta_pp/edgecollapse.h.reg b/source/blender/python/manta_pp/edgecollapse.h.reg new file mode 100644 index 00000000000..9869b6a5c7d --- /dev/null +++ b/source/blender/python/manta_pp/edgecollapse.h.reg @@ -0,0 +1 @@ +#include "edgecollapse.h" diff --git a/source/blender/python/manta_pp/edgecollapse.h.reg.cpp b/source/blender/python/manta_pp/edgecollapse.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/edgecollapse.h.reg.cpp diff --git a/source/blender/python/manta_pp/fastmarch.cpp b/source/blender/python/manta_pp/fastmarch.cpp new file mode 100644 index 00000000000..66612edb7ed --- /dev/null +++ b/source/blender/python/manta_pp/fastmarch.cpp @@ -0,0 +1,438 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Fast marching and extrapolation + * + ******************************************************************************/ + +#include "fastmarch.h" +#include "levelset.h" +#include "kernel.h" +#include <algorithm> + +using namespace std; + +namespace Manta { + +template<class COMP, int TDIR> +FastMarch<COMP,TDIR>::FastMarch(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& levelset, Real maxTime, + MACGrid* velTransport, Grid<Real>* velMag ) + : mLevelset(levelset), mFlags(flags), mFmFlags(fmFlags) +{ + if (velTransport) + mVelTransport.initMarching(velTransport, &flags); + if (velMag) + mMagTransport.initMarching(velMag, &flags); + + mMaxTime = maxTime * TDIR; +} + +// helper for individual components to calculateDistance +template<class COMP, int TDIR> template<int C> +Real FastMarch<COMP,TDIR>::calcWeights(int& okcnt, int& invcnt, Real* v, const Vec3i& idx) { + Real val = 0.; + Vec3i idxPlus(idx), idxMinus(idx); + idxPlus[C]++; + idxMinus[C]--; + + mWeights[C*2] = mWeights[C*2+1] = 0.; + if (mFmFlags(idxPlus)==FlagInited) { + // somewhat arbitrary - choose +1 value over -1 ... + val = mLevelset(idxPlus); + v[okcnt] = val; okcnt++; + mWeights[C*2] = 1.; + } else if (mFmFlags(idxMinus)==FlagInited) { + val = mLevelset(idxMinus); + v[okcnt] = val; okcnt++; + mWeights[C*2+1] = 1.; + } + else { + invcnt++; + } + return val; +} + +template<class COMP, int TDIR> +inline Real FastMarch<COMP,TDIR>::calculateDistance(const Vec3i& idx) { + //int invflag = 0; + int invcnt = 0; + Real v[3]; + int okcnt = 0; + + Real aVal = calcWeights<0>(okcnt, invcnt, v, idx); + Real bVal = calcWeights<1>(okcnt, invcnt, v, idx); + Real cVal = 0.; + if (mLevelset.is3D()) cVal = calcWeights<2>(okcnt, invcnt, v, idx); + else { invcnt++; mWeights[4] = mWeights[5] = 0.; } + + Real ret = InvalidTime(); + switch(invcnt) { + case 0: { + // take all values + const Real ca=v[0], cb=v[1], cc=v[2]; + const Real csqrt = max(0. , + -2.*(ca*ca+cb*cb- cb*cc + cc*cc - ca*(cb+cc)) + 3 ); + // clamp to make sure the sqrt is valid + ret = 0.333333*( ca+cb+cc+ TDIR*sqrt(csqrt) ); + + // weights needed for transport (transpTouch) + mWeights[0] *= fabs(ret-ca); + mWeights[1] *= fabs(ret-ca); + mWeights[2] *= fabs(ret-cb); + mWeights[3] *= fabs(ret-cb); + mWeights[4] *= fabs(ret-cc); + mWeights[5] *= fabs(ret-cc); + + Real norm = 0.0; // try to force normalization + for(int i=0;i<6;i++) { + norm += mWeights[i]; + } + norm = 1.0/norm; + for(int i=0;i<6;i++) { mWeights[i] *= norm; } + + } break; + case 1: { + // take just the 2 ok values + // t=0.5*( a+b+ (2*g*g-(b-a)*(b-a))^0.5) + const Real csqrt = max(0. , 2.-(v[1]-v[0])*(v[1]-v[0]) ); + // clamp to make sure the sqrt is valid + ret = 0.5*( v[0]+v[1]+ TDIR*sqrt(csqrt) ); + + // weights needed for transport (transpTouch) + mWeights[0] *= fabs(ret-aVal); + mWeights[1] *= fabs(ret-aVal); + mWeights[2] *= fabs(ret-bVal); + mWeights[3] *= fabs(ret-bVal); + mWeights[4] *= fabs(ret-cVal); + mWeights[5] *= fabs(ret-cVal); + + Real norm = 0.0; // try to force normalization + for(int i=0;i<6;i++) { + norm += mWeights[i]; + } + norm = 1.0/norm; + for(int i=0;i<6;i++) { mWeights[i] *= norm; } + // */ + + } break; + case 2: { + // just use the one remaining value + ret = v[0]+ (Real)(TDIR) ; // direction = +- 1 + } break; + default: + throw Error("FastMarch :: Invalid invcnt"); + break; + } + return ret; +} + +template<class COMP, int TDIR> +void FastMarch<COMP,TDIR>::addToList(const Vec3i& p, const Vec3i& src) { + if (!mLevelset.isInBounds(p,1)) return; + const int idx = mLevelset.index(p); + + // already known value, value alreay set to valid value? skip cell... + if(mFmFlags[idx] == FlagInited) return; + + // discard by source time now , TODO do instead before calling all addtolists? + Real srct = mLevelset(src); + if(COMP::compare(srct, mMaxTime)) return; + + Real ttime = calculateDistance(p); + + // remove old entry if larger + bool found=false; + + Real oldt = mLevelset[idx]; + if (mFmFlags[idx] == FlagIsOnHeap) { + found = true; + // is old time better? + if(COMP::compare(ttime,oldt)) return; + } + + // update field + mFmFlags[idx] = FlagIsOnHeap; + mLevelset[idx] = ttime; + + if (mVelTransport.isInitialized()) + mVelTransport.transpTouch(p.x, p.y, p.z, mWeights, ttime); + if (mMagTransport.isInitialized()) + mMagTransport.transpTouch(p.x, p.y, p.z, mWeights, ttime); + + // the following adds entries to the heap of active cells + // current: (!found) , previous: always add, might lead to duplicate + // entries, but the earlier will be handled earlier, the second one will skip to the FlagInited check above + if(!found) + { + // add list entry with source value + COMP entry; + entry.p = p; + entry.time = mLevelset[idx]; + + mHeap.push( entry ); + // debug info std::cout<<"push "<< entry.p <<","<< entry.time <<"\n"; + } + +} + +//! Enforce delta_phi = 0 on boundaries + + struct SetLevelsetBoundaries : public KernelBase { SetLevelsetBoundaries(LevelsetGrid& phi) : KernelBase(&phi,0) ,phi(phi) { run(); } inline void op(int i, int j, int k, LevelsetGrid& phi ) { + if (i==0) phi(i,j,k) = phi(1,j,k); + if (i==maxX-1) phi(i,j,k) = phi(i-1,j,k); + + if (j==0) phi(i,j,k) = phi(i,1,k); + if (j==maxY-1) phi(i,j,k) = phi(i,j-1,k); + + if(phi.is3D()) { + if (k==0) phi(i,j,k) = phi(i,j,1); + if (k==maxZ-1) phi(i,j,k) = phi(i,j,k-1); + } +} inline LevelsetGrid& getArg0() { return phi; } typedef LevelsetGrid type0; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, phi); } LevelsetGrid& phi; }; + +/*****************************************************************************/ +//! Walk... +template<class COMP, int TDIR> +void FastMarch<COMP,TDIR>::performMarching() { + mReheapVal = 0.0; + while(mHeap.size() > 0) { + + const COMP& ce = mHeap.top(); + Vec3i p = ce.p; + mFmFlags(p) = FlagInited; + mHeap.pop(); + // debug info std::cout<<"pop "<< ce.p <<","<< ce.time <<"\n"; + + addToList(Vec3i(p.x-1,p.y,p.z), p); + addToList(Vec3i(p.x+1,p.y,p.z), p); + addToList(Vec3i(p.x,p.y-1,p.z), p); + addToList(Vec3i(p.x,p.y+1,p.z), p); + if(mLevelset.is3D()) { + addToList(Vec3i(p.x,p.y,p.z-1), p); + addToList(Vec3i(p.x,p.y,p.z+1), p); + } + } + + // set boundary for plain array + SetLevelsetBoundaries setls(mLevelset); + setls.getArg0(); // get rid of compiler warning... +} + +// explicit instantiation +template class FastMarch<FmHeapEntryIn, -1>; +template class FastMarch<FmHeapEntryOut, +1>; + + +/*****************************************************************************/ +// simpler extrapolation functions (primarily for FLIP) + + + + struct knExtrapolateMACSimple : public KernelBase { knExtrapolateMACSimple(MACGrid& vel, int distance , Grid<int>& tmp , const int d , const int c ) : KernelBase(&vel,1) ,vel(vel),distance(distance),tmp(tmp),d(d),c(c) { run(); } inline void op(int i, int j, int k, MACGrid& vel, int distance , Grid<int>& tmp , const int d , const int c ) { + static const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + const int dim = (vel.is3D() ? 3:2); + + if (tmp(i,j,k) != 0) return; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + Real avgVel = 0.; + for (int n=0; n<2*dim; ++n) { + if (tmp(p+nb[n]) == d) { + //vel(p)[c] = (c+1.)*0.1; + avgVel += vel(p+nb[n])[c]; + nbs++; + } + } + + if(nbs>0) { + tmp(p) = d+1; + vel(p)[c] = avgVel / nbs; + } +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline int& getArg1() { return distance; } typedef int type1;inline Grid<int>& getArg2() { return tmp; } typedef Grid<int> type2;inline const int& getArg3() { return d; } typedef int type3;inline const int& getArg4() { return c; } typedef int type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,distance,tmp,d,c); } MACGrid& vel; int distance; Grid<int>& tmp; const int d; const int c; }; + + + struct knExtrapolateIntoBnd : public KernelBase { knExtrapolateIntoBnd(FlagGrid& flags, MACGrid& vel) : KernelBase(&flags,0) ,flags(flags),vel(vel) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel ) { + int c=0; + Vec3 v(0,0,0); + if( i==0 ) { + v = vel(i+1,j,k); + if(v[0] < 0.) v[0] = 0.; + c++; + } + else if( i==(flags.getSizeX()-1) ) { + v = vel(i-1,j,k); + if(v[0] > 0.) v[0] = 0.; + c++; + } + if( j==0 ) { + v = vel(i,j+1,k); + if(v[1] < 0.) v[1] = 0.; + c++; + } + else if( j==(flags.getSizeY()-1) ) { + v = vel(i,j-1,k); + if(v[1] > 0.) v[1] = 0.; + c++; + } + if(flags.is3D()) { + if( k==0 ) { + v = vel(i,j,k+1); + if(v[2] < 0.) v[2] = 0.; + c++; + } + else if( k==(flags.getSizeY()-1) ) { + v = vel(i,j,k-1); + if(v[2] > 0.) v[2] = 0.; + c++; + } } + if(c>0) { + vel(i,j,k) = v/(Real)c; + } +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,vel); } FlagGrid& flags; MACGrid& vel; }; + +inline Vec3 getNormal(const Grid<Real>& data, int i, int j, int k) { + if (i > data.getSizeX()-2) i= data.getSizeX()-2; + if (i < 1) i = 1; + if (j > data.getSizeY()-2) j= data.getSizeY()-2; + if (j < 1) j = 1; + + int kd = 1; + if(data.is3D()) { + if (k > data.getSizeZ()-2) k= data.getSizeZ()-2; + if (k < 1) k = 1; + } else { kd=0; } + + return Vec3( data(i+1,j ,k ) - data(i-1,j ,k ) , + data(i ,j+1,k ) - data(i ,j-1,k ) , + data(i ,j ,k+kd) - data(i ,j ,k-kd) ); +} + + + struct knUnprojectNormalComp : public KernelBase { knUnprojectNormalComp(FlagGrid& flags, MACGrid& vel, LevelsetGrid& phi, Real maxDist) : KernelBase(&flags,1) ,flags(flags),vel(vel),phi(phi),maxDist(maxDist) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, LevelsetGrid& phi, Real maxDist ) { + // apply inside, within range near obstacle surface + if(phi(i,j,k)>0. || phi(i,j,k)<-maxDist) return; + + Vec3 n = getNormal(phi, i,j,k); + Vec3 v = vel(i,j,k); + if(dot(n,v) < 0.) { + normalize(n); + Real l = dot(n,v); + vel(i,j,k) -= n*l; + } +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline LevelsetGrid& getArg2() { return phi; } typedef LevelsetGrid type2;inline Real& getArg3() { return maxDist; } typedef Real type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,phi,maxDist); } FlagGrid& flags; MACGrid& vel; LevelsetGrid& phi; Real maxDist; }; +// a simple extrapolation step , used for cases where there's no levelset +// (note, less accurate than fast marching extrapolation!) + +void extrapolateMACSimple(FlagGrid& flags, MACGrid& vel, int distance = 4, LevelsetGrid* phiObs=NULL ) { + Grid<int> tmp( flags.getParent() ); + int dim = (flags.is3D() ? 3:2); + + for(int c=0; c<dim; ++c) { + Vec3i dir = 0; + dir[c] = 1; + tmp.clear(); + + // remove all fluid cells + FOR_IJK_BND(flags,1) { + Vec3i p(i,j,k); + if (flags.isFluid(p) || flags.isFluid(p-dir) ) { + tmp(p) = 1; + } + } + + // debug init! , enable for testing only - set varying velocities inside + //FOR_IJK_BND(flags,1) { if (tmp(i,j,k) == 0) continue; vel(i,j,k)[c] = (i+j+k+c+1.)*0.1; } + + // extrapolate for distance + for(int d=1; d<1+distance; ++d) { + knExtrapolateMACSimple(vel, distance, tmp, d, c); + } // d + } + + if(phiObs) { + knUnprojectNormalComp( flags, vel, *phiObs, distance ); + } + + // copy tangential values into sides + knExtrapolateIntoBnd(flags, vel); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "extrapolateMACSimple" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); int distance = _args.getOpt<int >("distance",2,4,&_lock); LevelsetGrid* phiObs = _args.getPtrOpt<LevelsetGrid >("phiObs",3,NULL ,&_lock); _retval = getPyNone(); extrapolateMACSimple(flags,vel,distance,phiObs); _args.check(); } pbFinalizePlugin(parent,"extrapolateMACSimple" ); return _retval; } catch(std::exception& e) { pbSetError("extrapolateMACSimple",e.what()); return 0; } } static const Pb::Register _RP_extrapolateMACSimple ("","extrapolateMACSimple",_W_0); + + + + struct knExtrapolateMACFromWeight : public KernelBase { knExtrapolateMACFromWeight( MACGrid& vel, Grid<Vec3>& weight, int distance , const int d, const int c ) : KernelBase(&vel,1) ,vel(vel),weight(weight),distance(distance),d(d),c(c) { run(); } inline void op(int i, int j, int k, MACGrid& vel, Grid<Vec3>& weight, int distance , const int d, const int c ) { + static const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + const int dim = (vel.is3D() ? 3:2); + + if (weight(i,j,k)[c] != 0) return; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + Real avgVel = 0.; + for (int n=0; n<2*dim; ++n) { + if (weight(p+nb[n])[c] == d) { + avgVel += vel(p+nb[n])[c]; + nbs++; + } + } + + if(nbs>0) { + weight(p)[c] = d+1; + vel(p)[c] = avgVel / nbs; + } +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline Grid<Vec3>& getArg1() { return weight; } typedef Grid<Vec3> type1;inline int& getArg2() { return distance; } typedef int type2;inline const int& getArg3() { return d; } typedef int type3;inline const int& getArg4() { return c; } typedef int type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,weight,distance,d,c); } MACGrid& vel; Grid<Vec3>& weight; int distance; const int d; const int c; }; +// same as extrapolateMACSimple, but uses weight vec3 grid instead of flags to check +// for valid values (to be used in combination with mapPartsToMAC) +// note - the weight grid values are destroyed! the function is necessary due to discrepancies +// between velocity mapping on surface-levelset / fluid-flag creation. With this +// extrapolation we make sure the fluid region is covered by initial velocities + +void extrapolateMACFromWeight( MACGrid& vel, Grid<Vec3>& weight, int distance = 2) { + const int dim = (vel.is3D() ? 3:2); + + for(int c=0; c<dim; ++c) { + Vec3i dir = 0; + dir[c] = 1; + + // reset weight values to 0 (uninitialized), and 1 (initialized inner values) + FOR_IJK_BND(vel,1) { + Vec3i p(i,j,k); + if(weight(p)[c]>0.) weight(p)[c] = 1.0; + } + + // extrapolate for distance + for(int d=1; d<1+distance; ++d) { + knExtrapolateMACFromWeight(vel, weight, distance, d, c); + } // d + + } +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "extrapolateMACFromWeight" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Vec3>& weight = *_args.getPtr<Grid<Vec3> >("weight",1,&_lock); int distance = _args.getOpt<int >("distance",2,2,&_lock); _retval = getPyNone(); extrapolateMACFromWeight(vel,weight,distance); _args.check(); } pbFinalizePlugin(parent,"extrapolateMACFromWeight" ); return _retval; } catch(std::exception& e) { pbSetError("extrapolateMACFromWeight",e.what()); return 0; } } static const Pb::Register _RP_extrapolateMACFromWeight ("","extrapolateMACFromWeight",_W_1); + +} // namespace + + diff --git a/source/blender/python/manta_pp/fastmarch.h b/source/blender/python/manta_pp/fastmarch.h new file mode 100644 index 00000000000..9e8cf22c8c5 --- /dev/null +++ b/source/blender/python/manta_pp/fastmarch.h @@ -0,0 +1,197 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Fast marching + * + ******************************************************************************/ + +#ifndef _FASTMARCH_H +#define _FASTMARCH_H + +#include <queue> +#include "levelset.h" + +namespace Manta { + +//! Fast marching. Transport certain values +// This class exists in two versions: for scalar, and for vector values - the only difference are +// flag checks i transpTouch (for simplicity in separate classes) + +template<class GRID, class T> +inline T fmInterpolateNeighbors(GRID* mpVal, int x,int y,int z, Real *weights) { + T val(0.); + if(weights[0]>0.0) val += mpVal->get(x+1, y+0, z+0) * weights[0]; + if(weights[1]>0.0) val += mpVal->get(x-1, y+0, z+0) * weights[1]; + if(weights[2]>0.0) val += mpVal->get(x+0, y+1, z+0) * weights[2]; + if(weights[3]>0.0) val += mpVal->get(x+0, y-1, z+0) * weights[3]; + if(mpVal->is3D()) { + if(weights[4]>0.0) val += mpVal->get(x+0, y+0, z+1) * weights[4]; + if(weights[5]>0.0) val += mpVal->get(x+0, y+0, z-1) * weights[5]; + } + return val; +} + +template<class GRID, class T> +class FmValueTransportScalar { +public: + FmValueTransportScalar() : mpVal(0),mpFlags(0) { }; + ~FmValueTransportScalar() { }; + void initMarching(GRID* val, FlagGrid* flags) { + mpVal = val; + mpFlags = flags; + } + inline bool isInitialized() { return mpVal != 0; } + + //! cell is touched by marching from source cell + inline void transpTouch(int x,int y,int z, Real *weights, Real time) { + if(!mpVal || !mpFlags->isEmpty(x,y,z)) return; + T val = fmInterpolateNeighbors<GRID,T>(mpVal,x,y,z,weights); + (*mpVal)(x,y,z) = val; + }; +protected: + GRID* mpVal; + FlagGrid* mpFlags; +}; + +template<class GRID, class T> +class FmValueTransportVec3 { +public: + FmValueTransportVec3() : mpVal(0), mpFlags(0) { }; + ~FmValueTransportVec3() { }; + inline bool isInitialized() { return mpVal != 0; } + void initMarching(GRID* val, FlagGrid* flags) { + mpVal = val; + mpFlags = flags; + } + + //! cell is touched by marching from source cell + inline void transpTouch(int x,int y,int z, Real *weights, Real time) { + if(!mpVal || !mpFlags->isEmpty(x,y,z)) return; + + T val = fmInterpolateNeighbors<GRID,T>(mpVal,x,y,z,weights); + + // set velocity components if adjacent is empty + if (mpFlags->isEmpty(x-1,y,z)) (*mpVal)(x,y,z).x = val.x; + if (mpFlags->isEmpty(x,y-1,z)) (*mpVal)(x,y,z).y = val.y; + if(mpVal->is3D()) { if (mpFlags->isEmpty(x,y,z-1)) (*mpVal)(x,y,z).z = val.z; } + }; + +protected: + GRID* mpVal; + FlagGrid* mpFlags; +}; + +class FmHeapEntryOut { +public: + Vec3i p; + // quick time access for sorting + Real time; + static inline bool compare(const Real x, const Real y) { + return x > y; + } + + inline bool operator< (const FmHeapEntryOut& o) const { + const Real d = fabs((time) - ((o.time))); + if (d > 0.) return (time) > ((o.time)); + if (p.z != o.p.z) return p.z > o.p.z; + if (p.y != o.p.y) return p.y > o.p.y; + return p.x > o.p.x; + }; + +}; + +class FmHeapEntryIn { +public: + Vec3i p; + // quick time access for sorting + Real time; + static inline bool compare(const Real x, const Real y) { + return x < y; + } + + inline bool operator< (const FmHeapEntryIn& o) const { + const Real d = fabs((time) - ((o.time))); + if (d > 0.) return (time) < ((o.time)); + if (p.z != o.p.z) return p.z < o.p.z; + if (p.y != o.p.y) return p.y < o.p.y; + return p.x < o.p.x; + }; +}; + + +//! fast marching algorithm wrapper class +template<class T, int TDIR> +class FastMarch { + +public: + // MSVC doesn't allow static const variables in template classes + static inline Real InvalidTime() { return -1000; } + static inline Real InvtOffset() { return 500; } + + enum SpecialValues { FlagInited = 1, FlagIsOnHeap = 2}; + + FastMarch(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& levelset, Real maxTime, + MACGrid* velTransport = NULL, Grid<Real>* velMag = NULL); + ~FastMarch() {} + + //! advect level set function with given velocity */ + void performMarching(); + + //! test value for invalidity + inline bool isInvalid(Real v) const { return (v <= InvalidTime()); } + + void addToList(const Vec3i& p, const Vec3i& src); + + //! convert phi to time value + inline Real phi2time(Real phival) { return (phival-InvalidTime()+ InvtOffset()) * -1.0; } + + //! ... and back + inline Real time2phi(Real tval) { return (InvalidTime() - InvtOffset() - tval); } + + inline Real _phi(int i, int j, int k) { return mLevelset(i,j,k); } +protected: + LevelsetGrid& mLevelset; + FlagGrid& mFlags; + Grid<int>& mFmFlags; + + //! velocity extrpolation + FmValueTransportVec3<MACGrid , Vec3> mVelTransport; + FmValueTransportScalar<Grid<Real>, Real> mMagTransport; + + //! maximal time to march for + Real mMaxTime; + + //! fast marching list + std::priority_queue<T, std::vector<T>, std::less<T> > mHeap; + Real mReheapVal; + + //! weights for touching points + Real mWeights[6]; + + template<int C> inline Real calcWeights(int& okCnt, int& invcnt, Real* v, const Vec3i& idx); + + inline Real calculateDistance(const Vec3i& pos); +}; + +} // namespace +#endif + + + diff --git a/source/blender/python/manta_pp/fastmarch.h.reg b/source/blender/python/manta_pp/fastmarch.h.reg new file mode 100644 index 00000000000..7b9296f3580 --- /dev/null +++ b/source/blender/python/manta_pp/fastmarch.h.reg @@ -0,0 +1 @@ +#include "fastmarch.h" diff --git a/source/blender/python/manta_pp/fastmarch.h.reg.cpp b/source/blender/python/manta_pp/fastmarch.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/fastmarch.h.reg.cpp diff --git a/source/blender/python/manta_pp/fileio.cpp b/source/blender/python/manta_pp/fileio.cpp new file mode 100644 index 00000000000..fbc9d7adf97 --- /dev/null +++ b/source/blender/python/manta_pp/fileio.cpp @@ -0,0 +1,771 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Loading and writing grids and meshes to disk + * + ******************************************************************************/ + +#include <iostream> +#include <fstream> +#include <cstdlib> +#if NO_ZLIB!=1 +extern "C" { +#include <zlib.h> +} +#endif + +#include "fileio.h" +#include "grid.h" +#include "mesh.h" +#include "vortexsheet.h" +#include "particle.h" +#include <cstring> + +using namespace std; + +namespace Manta { + +//***************************************************************************** +// mesh data +//***************************************************************************** + +void readBobjFile(const string& name, Mesh* mesh, bool append) { + debMsg( "reading mesh file " << name ,1); + if (!append) + mesh->clear(); + else + errMsg("readBobj: append not yet implemented!"); + +# if NO_ZLIB!=1 + const Real dx = mesh->getParent()->getDx(); + const Vec3 gs = toVec3( mesh->getParent()->getGridSize() ); + + gzFile gzf = gzopen(name.c_str(), "rb1"); // do some compression + if (!gzf) + errMsg("readBobj: unable to open file"); + + // read vertices + int num = 0; + gzread(gzf, &num, sizeof(int)); + mesh->resizeNodes(num); + debMsg( "read mesh , verts "<<num,1); + for (int i=0; i<num; i++) { + Vector3D<float> pos; + gzread(gzf, &pos.value[0], sizeof(float)*3); + mesh->nodes(i).pos = toVec3(pos); + + // convert to grid space + mesh->nodes(i).pos /= dx; + mesh->nodes(i).pos += gs*0.5; + } + + // normals + num = 0; + gzread(gzf, &num, sizeof(int)); + for (int i=0; i<num; i++) { + Vector3D<float> pos; + gzread(gzf, &pos.value[0], sizeof(float)*3); + mesh->nodes(i).normal = toVec3(pos); + } + + // read tris + num = 0; + gzread(gzf, &num, sizeof(int)); + mesh->resizeTris( num ); + for(int t=0; t<num; t++) { + for(int j=0; j<3; j++) { + int trip = 0; + gzread(gzf, &trip, sizeof(int)); + mesh->tris(t).c[j] = trip; + } + } + // note - vortex sheet info ignored for now... (see writeBobj) + gzclose( gzf ); + debMsg( "read mesh , triangles "<<mesh->numTris()<<", vertices "<<mesh->numNodes()<<" ",1 ); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +} + +void writeBobjFile(const string& name, Mesh* mesh) { + debMsg( "writing mesh file " << name ,1); +# if NO_ZLIB!=1 + const Real dx = mesh->getParent()->getDx(); + const Vec3i gs = mesh->getParent()->getGridSize(); + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) + errMsg("writeBobj: unable to open file"); + + // write vertices + int numVerts = mesh->numNodes(); + gzwrite(gzf, &numVerts, sizeof(int)); + for (int i=0; i<numVerts; i++) { + Vector3D<float> pos = toVec3f(mesh->nodes(i).pos); + // normalize to unit cube around 0 + pos -= toVec3f(gs)*0.5; + pos *= dx; + gzwrite(gzf, &pos.value[0], sizeof(float)*3); + } + + // normals + mesh->computeVertexNormals(); + gzwrite(gzf, &numVerts, sizeof(int)); + for (int i=0; i<numVerts; i++) { + Vector3D<float> pos = toVec3f(mesh->nodes(i).normal); + gzwrite(gzf, &pos.value[0], sizeof(float)*3); + } + + // write tris + int numTris = mesh->numTris(); + gzwrite(gzf, &numTris, sizeof(int)); + for(int t=0; t<numTris; t++) { + for(int j=0; j<3; j++) { + int trip = mesh->tris(t).c[j]; + gzwrite(gzf, &trip, sizeof(int)); + } + } + + // per vertex smoke densities + if (mesh->getType() == Mesh::TypeVortexSheet) { + VortexSheetMesh* vmesh = (VortexSheetMesh*) mesh; + int densId[4] = {0, 'v','d','e'}; + gzwrite(gzf, &densId[0], sizeof(int) * 4); + + // compute densities + vector<float> triDensity(numTris); + for (int tri=0; tri < numTris; tri++) { + Real area = vmesh->getFaceArea(tri); + if (area>0) + triDensity[tri] = vmesh->sheet(tri).smokeAmount; + } + + // project triangle data to vertex + vector<int> triPerVertex(numVerts); + vector<float> density(numVerts); + for (int tri=0; tri < numTris; tri++) { + for (int c=0; c<3; c++) { + int vertex = mesh->tris(tri).c[c]; + density[vertex] += triDensity[tri]; + triPerVertex[vertex]++; + } + } + + // averaged smoke densities + for(int point=0; point<numVerts; point++) { + float dens = 0; + if (triPerVertex[point]>0) + dens = density[point] / triPerVertex[point]; + gzwrite(gzf, &dens, sizeof(float)); + } + } + + // vertex flags + if (mesh->getType() == Mesh::TypeVortexSheet) { + int Id[4] = {0, 'v','x','f'}; + gzwrite(gzf, &Id[0], sizeof(int) * 4); + + // averaged smoke densities + for(int point=0; point<numVerts; point++) { + float alpha = (mesh->nodes(point).flags & Mesh::NfMarked) ? 1: 0; + gzwrite(gzf, &alpha, sizeof(float)); + } + } + + gzclose( gzf ); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +} + +void readObjFile(const std::string& name, Mesh* mesh, bool append) { + ifstream ifs (name.c_str()); + + if (!ifs.good()) + errMsg("can't open file '" + name + "'"); + + if (!append) + mesh->clear(); + int nodebase = mesh->numNodes(); + while(ifs.good() && !ifs.eof()) { + string id; + ifs >> id; + + if (id[0] == '#') { + // comment + getline(ifs, id); + continue; + } + if (id == "vt") { + // tex coord, ignore + } else if (id == "vn") { + // normals, ignore + } else if (id == "v") { + // vertex + Node n; + ifs >> n.pos.x >> n.pos.y >> n.pos.z; + mesh->addNode(n); + } else if (id == "g") { + // group + string group; + ifs >> group; + } else if (id == "f") { + // face + string face; + Triangle t; + for (int i=0; i<3; i++) { + ifs >> face; + if (face.find('/') != string::npos) + face = face.substr(0, face.find('/')); // ignore other indices + int idx = atoi(face.c_str()) - 1; + if (idx < 0) + errMsg("invalid face encountered"); + idx += nodebase; + t.c[i] = idx; + } + mesh->addTri(t); + } else { + // whatever, ignore + } + // kill rest of line + getline(ifs, id); + } + ifs.close(); +} + +void writeObjFile(const string& name, Mesh* mesh) { + errMsg("obj exporter not yet implemented"); +} + +//***************************************************************************** +// grid data +//***************************************************************************** + +template<class T> +void writeGridTxt(const string& name, Grid<T>* grid) { + debMsg( "writing grid " << grid->getName() << " to text file " << name ,1); + + ofstream ofs(name.c_str()); + if (!ofs.good()) + errMsg("can't open file!"); + FOR_IJK(*grid) { + ofs << Vec3i(i,j,k) <<" = "<< (*grid)(i,j,k) <<"\n"; + } + ofs.close(); +} + +template<class T> +void writeGridRaw(const string& name, Grid<T>* grid) { + debMsg( "writing grid " << grid->getName() << " to raw file " << name ,1); + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + gzwrite(gzf, &((*grid)[0]), sizeof(T)*grid->getSizeX()*grid->getSizeY()*grid->getSizeZ()); + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +} + +template<class T> +void readGridRaw(const string& name, Grid<T>* grid) { + debMsg( "reading grid " << grid->getName() << " from raw file " << name ,1); + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + int bytes = sizeof(T)*grid->getSizeX()*grid->getSizeY()*grid->getSizeZ(); + int readBytes = gzread(gzf, &((*grid)[0]), bytes); + assertMsg(bytes==readBytes, "can't read raw file, stream length does not match"<<bytes<<" vs "<<readBytes); + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +} + +//! legacy headers for reading old files +typedef struct { + int dimX, dimY, dimZ; + int frames, elements, elementType, bytesPerElement, bytesPerFrame; +} UniLegacyHeader; + +typedef struct { + int dimX, dimY, dimZ; + int gridType, elementType, bytesPerElement; +} UniLegacyHeader2; + +//! uni file header +typedef struct { + int dimX, dimY, dimZ; // grid size + int gridType, elementType, bytesPerElement; // data type info + char info[256]; // mantaflow build information + unsigned long long timestamp; // creation time +} UniHeader; + +//! for test run debugging +void printUniFileInfoString(const string& name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (gzf) { + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + if (!strcmp(ID, "MNT2")) { + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + gzclose(gzf); + debMsg("File '"<<name<<"' info: "<< head.info ,1); + return; // all good! + } + gzclose(gzf); + } +# endif + debMsg("File '"<<name<<"', no valid info string found",1); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "printUniFileInfoString" ); PyObject *_retval = 0; { ArgLocker _lock; const string& name = _args.get<string >("name",0,&_lock); _retval = getPyNone(); printUniFileInfoString(name); _args.check(); } pbFinalizePlugin(parent,"printUniFileInfoString" ); return _retval; } catch(std::exception& e) { pbSetError("printUniFileInfoString",e.what()); return 0; } } static const Pb::Register _RP_printUniFileInfoString ("","printUniFileInfoString",_W_0); + +//! for auto-init & check of results of test runs +Vec3 getUniFileSize(const string& name) { + Vec3 s(0.); +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (gzf) { + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + if (!strcmp(ID, "MNT2")) { + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + s = Vec3(head.dimX,head.dimY,head.dimZ); + } + gzclose(gzf); + } +# endif + return s; +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "getUniFileSize" ); PyObject *_retval = 0; { ArgLocker _lock; const string& name = _args.get<string >("name",0,&_lock); _retval = toPy(getUniFileSize(name)); _args.check(); } pbFinalizePlugin(parent,"getUniFileSize" ); return _retval; } catch(std::exception& e) { pbSetError("getUniFileSize",e.what()); return 0; } } static const Pb::Register _RP_getUniFileSize ("","getUniFileSize",_W_1); + +#if NO_ZLIB!=1 +template <class T> +void convertDoubleAndWrite(Grid<T>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + errMsg("unknown type, not yet supported"); +} + +template <> +void convertDoubleAndWrite(Grid<int>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + gzwrite(gzf, &head, sizeof(UniHeader)); + gzwrite(gzf, ptr, sizeof(int)*head.dimX*head.dimY*head.dimZ); +} + +template <> +void convertDoubleAndWrite(Grid<double>& grid, void* ptr, gzFile& gzf, UniHeader& head) { + head.bytesPerElement = sizeof(float); + gzwrite(gzf, &head, sizeof(UniHeader)); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i,++ptrf) { + *ptrf = (float)grid[i]; + } + gzwrite(gzf, ptr, sizeof(float)* head.dimX*head.dimY*head.dimZ); +} + +template <> +void convertDoubleAndWrite(Grid<Vector3D<double> >& grid, void* ptr, gzFile& gzf, UniHeader& head) { + head.bytesPerElement = sizeof(Vector3D<float>); + gzwrite(gzf, &head, sizeof(UniHeader)); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i) { + for(int c=0; c<3; ++c) { *ptrf = (float)grid[i][c]; ptrf++; } + } + gzwrite(gzf, ptr, sizeof(float)*3 *head.dimX*head.dimY*head.dimZ); +} +#endif // NO_ZLIB!=1 + +template <class T> +void writeGridUni(const string& name, Grid<T>* grid) { + debMsg( "writing grid " << grid->getName() << " to uni file " << name ,1); + +# if NO_ZLIB!=1 + char ID[5] = "MNT2"; + UniHeader head; + head.dimX = grid->getSizeX(); + head.dimY = grid->getSizeY(); + head.dimZ = grid->getSizeZ(); + head.gridType = grid->getType(); + head.bytesPerElement = sizeof(T); + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + if (grid->getType() & GridBase::TypeInt) + head.elementType = 0; + else if (grid->getType() & GridBase::TypeReal) + head.elementType = 1; + else if (grid->getType() & GridBase::TypeVec3) + head.elementType = 2; + else + errMsg("unknown element type"); + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + void* ptr = &((*grid)[0]); +# if FLOATINGPOINT_PRECISION!=1 + // always write float values, even if compiled with double precision... + Grid<T> temp(grid->getParent()); + // "misuse" temp grid as storage for floating point values (we have double, so it will always fit) + //ptr = &(temp[0]); + //float* ptrf = (float*)ptr; + convertDoubleAndWrite( *grid, &(temp[0]), gzf, head); +# endif + gzwrite(gzf, &head, sizeof(UniHeader)); + gzwrite(gzf, ptr, sizeof(T)*head.dimX*head.dimY*head.dimZ); + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +}; + +// grid conversion functions for double precision +template <class T> +void convertFloatGridToDouble(Grid<T>& grid, void* ptr, int bytesPerElement) { + errMsg("unknown type, not yet supported"); +} + +template <> +void convertFloatGridToDouble<int>(Grid<int>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(int), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(int) ); + // easy, nothing to do for ints + memcpy(&(grid[0]), ptr, sizeof(int) * grid.getSizeX()*grid.getSizeY()*grid.getSizeZ() ); +} + +template <> +void convertFloatGridToDouble<double>(Grid<double>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(float), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(float) ); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i,++ptrf) { + grid[i] = (double)(*ptrf); + } +} + +template <> +void convertFloatGridToDouble<Vec3>(Grid<Vec3>& grid, void* ptr, int bytesPerElement) { + assertMsg (bytesPerElement == sizeof(Vector3D<float>), "grid element size doesn't match "<< bytesPerElement <<" vs "<< sizeof(Vector3D<float>) ); + float* ptrf = (float*)ptr; + for(int i=0; i<grid.getSizeX()*grid.getSizeY()*grid.getSizeZ(); ++i) { + Vec3 v; + for(int c=0; c<3; ++c) { v[c] = double(*ptrf); ptrf++; } + grid[i] = v; + } +} + +// make sure compatible grid types dont lead to errors... +static int unifyGridType(int type) { + // real <> levelset + if(type & GridBase::TypeReal) type |= GridBase::TypeLevelset; + if(type & GridBase::TypeLevelset) type |= GridBase::TypeReal; + // vec3 <> mac + if(type & GridBase::TypeVec3) type |= GridBase::TypeMAC; + if(type & GridBase::TypeMAC) type |= GridBase::TypeVec3; + return type; +} + +template <class T> +void readGridUni(const string& name, Grid<T>* grid) { + debMsg( "reading grid " << grid->getName() << " from uni file " << name ,1); + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "DDF2")) { + // legacy file format + UniLegacyHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniLegacyHeader)) == sizeof(UniLegacyHeader), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match"); + assertMsg (head.bytesPerElement * head.elements == sizeof(T), "grid type doesn't match"); + // skip flags + int numEl = head.dimX*head.dimY*head.dimZ; + gzseek(gzf, numEl, SEEK_CUR); + // actual grid read + gzread(gzf, &((*grid)[0]), sizeof(T)*numEl); + } + else if (!strcmp(ID, "MNT1")) { + // legacy file format 2 + UniLegacyHeader2 head; + assertMsg (gzread(gzf, &head, sizeof(UniLegacyHeader2)) == sizeof(UniLegacyHeader2), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match, "<< Vec3(head.dimX,head.dimY,head.dimZ)<<" vs "<< grid->getSize() ); + assertMsg (head.gridType == grid->getType(), "grid type doesn't match "<< head.gridType<<" vs "<< grid->getType() ); + assertMsg (head.bytesPerElement == sizeof(T), "grid element size doesn't match "<< head.bytesPerElement <<" vs "<< sizeof(T) ); + gzread(gzf, &((*grid)[0]), sizeof(T)*head.dimX*head.dimY*head.dimZ); + } + else if (!strcmp(ID, "MNT2")) { + // current file format + UniHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniHeader)) == sizeof(UniHeader), "can't read file, no header present"); + assertMsg (head.dimX == grid->getSizeX() && head.dimY == grid->getSizeY() && head.dimZ == grid->getSizeZ(), "grid dim doesn't match, "<< Vec3(head.dimX,head.dimY,head.dimZ)<<" vs "<< grid->getSize() ); + assertMsg ( unifyGridType(head.gridType)==unifyGridType(grid->getType()) , "grid type doesn't match "<< head.gridType<<" vs "<< grid->getType() ); +# if FLOATINGPOINT_PRECISION!=1 + // convert float to double + Grid<T> temp(grid->getParent()); + void* ptr = &(temp[0]); + gzread(gzf, ptr, sizeof(T)*head.dimX*head.dimY*head.dimZ); + convertFloatGridToDouble<T>(*grid, ptr, head.bytesPerElement); +# else + assertMsg (head.bytesPerElement == sizeof(T), "grid element size doesn't match "<< head.bytesPerElement <<" vs "<< sizeof(T) ); + gzread(gzf, &((*grid)[0]), sizeof(T)*head.dimX*head.dimY*head.dimZ); +# endif + } + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +}; + +template <class T> +void writeGridVol(const string& name, Grid<T>* grid) { + debMsg( "writing grid " << grid->getName() << " to vol file " << name ,1); + errMsg("Type not yet supported!"); +} + +struct volHeader { + char ID[3]; + char version; + int encoding; + int dimX, dimY, dimZ; + int channels; + Vec3 bboxMin, bboxMax; +}; + +template <> +void writeGridVol<Real>(const string& name, Grid<Real>* grid) { + debMsg( "writing real grid " << grid->getName() << " to vol file " << name ,1); + + volHeader header; + header.ID[0] = 'V'; + header.ID[1] = 'O'; + header.ID[2] = 'L'; + header.version = 3; + header.encoding = 1; // float32 precision + header.dimX = grid->getSizeX(); + header.dimY = grid->getSizeY(); + header.dimZ = grid->getSizeZ(); + header.channels = 1; // only 1 channel + header.bboxMin = Vec3(-0.5); + header.bboxMax = Vec3( 0.5); + + FILE* fp = fopen( name.c_str(), "wb" ); + if (fp == NULL) { + errMsg("Cannot open '" << name << "'"); + return; + } + + fwrite( &header, sizeof(volHeader), 1, fp ); + +# if FLOATINGPOINT_PRECISION==1 + // for float, write one big chunk + fwrite( &(*grid)[0], sizeof(float), grid->getSizeX()*grid->getSizeY()*grid->getSizeZ(), fp ); +# else + // explicitly convert each entry to float - we might have double precision in mantaflow + FOR_IDX(*grid) { + float value = (*grid)[idx]; + fwrite( &value, sizeof(float), 1, fp ); + } +# endif + + fclose(fp); +}; + + +//***************************************************************************** +// particle data +//***************************************************************************** + +//! in line with grid uni header +typedef struct { + int dim; // number of partilces + int dimX, dimY, dimZ; // underlying solver resolution (all data in local coordinates!) + int elementType, bytesPerElement; // type id and byte size + char info[256]; // mantaflow build information + unsigned long long timestamp; // creation time +} UniPartHeader; + +template <class T> +void writeParticlesUni(const std::string& name, BasicParticleSystem* parts ) { + debMsg( "writing particles " << parts->getName() << " to uni file " << name ,1); + +# if NO_ZLIB!=1 + char ID[5] = "PB02"; + UniPartHeader head; + head.dim = parts->size(); + Vec3i gridSize = parts->getParent()->getGridSize(); + head.dimX = gridSize.x; + head.dimY = gridSize.y; + head.dimZ = gridSize.z; + head.bytesPerElement = sizeof(T); + head.elementType = 0; // 0 for base data + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + gzwrite(gzf, &head, sizeof(UniPartHeader)); + gzwrite(gzf, &(parts->getData()[0]), sizeof(T)*head.dim); + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +}; + +template <class T> +void readParticlesUni(const std::string& name, BasicParticleSystem* parts ) { + debMsg( "reading particles " << parts->getName() << " from uni file " << name ,1); + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "PB01")) { + errMsg("particle uni file format v01 not supported anymore"); + } else if (!strcmp(ID, "PB02")) { + // current file format + UniPartHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniPartHeader)) == sizeof(UniPartHeader), "can't read file, no header present"); + assertMsg ( ((head.bytesPerElement == sizeof(T)) && (head.elementType==0) ), "particle type doesn't match"); + + // re-allocate all data + parts->resizeAll( head.dim ); + + assertMsg (head.dim == parts->size() , "particle size doesn't match"); + int bytes = sizeof(T)*head.dim; + int readBytes = gzread(gzf, &(parts->getData()[0]), sizeof(T)*head.dim); + assertMsg(bytes==readBytes, "can't read uni file, stream length does not match, "<<bytes<<" vs "<<readBytes ); + + parts->transformPositions( Vec3i(head.dimX,head.dimY,head.dimZ), parts->getParent()->getGridSize() ); + } + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +}; + +template <class T> +void writePdataUni(const std::string& name, ParticleDataImpl<T>* pdata ) { + debMsg( "writing particle data " << pdata->getName() << " to uni file " << name ,1); + +# if NO_ZLIB!=1 + char ID[5] = "PD01"; + UniPartHeader head; + head.dim = pdata->size(); + head.bytesPerElement = sizeof(T); + head.elementType = 1; // 1 for particle data, todo - add sub types? + snprintf( head.info, 256, "%s", buildInfoString().c_str() ); + MuTime stamp; stamp.get(); + head.timestamp = stamp.time; + + gzFile gzf = gzopen(name.c_str(), "wb1"); // do some compression + if (!gzf) errMsg("can't open file"); + + gzwrite(gzf, ID, 4); + gzwrite(gzf, &head, sizeof(UniPartHeader)); + gzwrite(gzf, &(pdata->get(0)), sizeof(T)*head.dim); + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +}; + +template <class T> +void readPdataUni(const std::string& name, ParticleDataImpl<T>* pdata ) { + debMsg( "reading particle data " << pdata->getName() << " from uni file " << name ,1); + +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "rb"); + if (!gzf) errMsg("can't open file"); + + char ID[5]={0,0,0,0,0}; + gzread(gzf, ID, 4); + + if (!strcmp(ID, "PD01")) { + UniPartHeader head; + assertMsg (gzread(gzf, &head, sizeof(UniPartHeader)) == sizeof(UniPartHeader), "can't read file, no header present"); + assertMsg ( ((head.bytesPerElement == sizeof(T)) && (head.elementType==1) ), "pdata type doesn't match"); + assertMsg (head.dim == pdata->size() , "pdata size doesn't match"); + int bytes = sizeof(T)*head.dim; + int readBytes = gzread(gzf, &(pdata->get(0)), sizeof(T)*head.dim); + assertMsg(bytes==readBytes, "can't read uni file, stream length does not match, "<<bytes<<" vs "<<readBytes ); + } + gzclose(gzf); +# else + debMsg( "file format not supported without zlib" ,1); +# endif +} + +// explicit instantiation +template void writeGridRaw<int> (const string& name, Grid<int>* grid); +template void writeGridRaw<Real>(const string& name, Grid<Real>* grid); +template void writeGridRaw<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridUni<int> (const string& name, Grid<int>* grid); +template void writeGridUni<Real>(const string& name, Grid<Real>* grid); +template void writeGridUni<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridVol<int> (const string& name, Grid<int>* grid); +template void writeGridVol<Vec3>(const string& name, Grid<Vec3>* grid); +template void writeGridTxt<int> (const string& name, Grid<int>* grid); +template void writeGridTxt<Real>(const string& name, Grid<Real>* grid); +template void writeGridTxt<Vec3>(const string& name, Grid<Vec3>* grid); +template void readGridRaw<int> (const string& name, Grid<int>* grid); +template void readGridRaw<Real> (const string& name, Grid<Real>* grid); +template void readGridRaw<Vec3> (const string& name, Grid<Vec3>* grid); +template void readGridUni<int> (const string& name, Grid<int>* grid); +template void readGridUni<Real> (const string& name, Grid<Real>* grid); +template void readGridUni<Vec3> (const string& name, Grid<Vec3>* grid); + +template void writeParticlesUni<BasicParticleData>(const std::string& name, BasicParticleSystem* parts ); +template void readParticlesUni<BasicParticleData> (const std::string& name, BasicParticleSystem* parts ); + +template void writePdataUni<int> (const std::string& name, ParticleDataImpl<int>* pdata ); +template void writePdataUni<Real>(const std::string& name, ParticleDataImpl<Real>* pdata ); +template void writePdataUni<Vec3>(const std::string& name, ParticleDataImpl<Vec3>* pdata ); +template void readPdataUni<int> (const std::string& name, ParticleDataImpl<int>* pdata ); +template void readPdataUni<Real> (const std::string& name, ParticleDataImpl<Real>* pdata ); +template void readPdataUni<Vec3> (const std::string& name, ParticleDataImpl<Vec3>* pdata ); + +#if ENABLE_GRID_TEST_DATATYPE==1 +// dummy functions for test datatype - not really supported right now! +// but we need some function body for linking +template<> void writeGridRaw<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridUni<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridVol<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void writeGridTxt<nbVector>(const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void readGridRaw<nbVector> (const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +template<> void readGridUni<nbVector> (const string& name, Grid<nbVector>* grid) {assertMsg(false,"Not supported right now.");}; +#endif // ENABLE_GRID_TEST_DATATYPE + + +} //namespace + + diff --git a/source/blender/python/manta_pp/fileio.h b/source/blender/python/manta_pp/fileio.h new file mode 100644 index 00000000000..d6c27e382a3 --- /dev/null +++ b/source/blender/python/manta_pp/fileio.h @@ -0,0 +1,62 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Loading and writing grids and meshes to disk + * + ******************************************************************************/ + +#ifndef _FILEIO_H +#define _FILEIO_H + +#include <string> + +namespace Manta { + +// forward decl. +class Mesh; +class FlagGrid; +template<class T> class Grid; +class BasicParticleSystem; +template<class T> class ParticleDataImpl; + +void writeObjFile(const std::string& name, Mesh* mesh); +void writeBobjFile(const std::string& name, Mesh* mesh); +void readObjFile(const std::string& name, Mesh* mesh, bool append); +void readBobjFile(const std::string& name, Mesh* mesh, bool append); + +template<class T> void writeGridRaw(const std::string& name, Grid<T>* grid); +template<class T> void writeGridUni(const std::string& name, Grid<T>* grid); +template<class T> void writeGridVol(const std::string& name, Grid<T>* grid); +template<class T> void writeGridTxt(const std::string& name, Grid<T>* grid); + +template<class T> void readGridUni(const std::string& name, Grid<T>* grid); +template<class T> void readGridRaw(const std::string& name, Grid<T>* grid); + +template <class T> void writeParticlesUni(const std::string& name, BasicParticleSystem* parts ); +template <class T> void readParticlesUni (const std::string& name, BasicParticleSystem* parts ); + +template <class T> void writePdataUni(const std::string& name, ParticleDataImpl<T>* pdata ); +template <class T> void readPdataUni (const std::string& name, ParticleDataImpl<T>* pdata ); + +} // namespace + +#endif + + diff --git a/source/blender/python/manta_pp/fileio.h.reg b/source/blender/python/manta_pp/fileio.h.reg new file mode 100644 index 00000000000..3c1a56af210 --- /dev/null +++ b/source/blender/python/manta_pp/fileio.h.reg @@ -0,0 +1 @@ +#include "fileio.h" diff --git a/source/blender/python/manta_pp/fileio.h.reg.cpp b/source/blender/python/manta_pp/fileio.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/fileio.h.reg.cpp diff --git a/source/blender/python/manta_pp/fluidsolver.cpp b/source/blender/python/manta_pp/fluidsolver.cpp new file mode 100644 index 00000000000..3374823bace --- /dev/null +++ b/source/blender/python/manta_pp/fluidsolver.cpp @@ -0,0 +1,137 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main class for the fluid solver + * + ******************************************************************************/ + +#include "fluidsolver.h" +#include "grid.h" +#include <sstream> +#include <fstream> + +using namespace std; +namespace Manta { + +#ifdef GUI + // defined in qtmain.cpp + extern void updateQtGui(bool full, int frame, const std::string& curPlugin); +#else + inline void updateQtGui(bool full, int frame, const std::string& curPlugin) {} +#endif + +//****************************************************************************** +// Gridstorage-related members + +template<class T> +void FluidSolver::GridStorage<T>::free() { + if (used != 0) + errMsg("can't clean grid cache, some grids are still in use"); + for(size_t i = 0; i<grids.size(); i++) + delete[] grids[i]; + grids.clear(); +} +template<class T> +T* FluidSolver::GridStorage<T>::get(Vec3i size) { + if ((int)grids.size() <= used) { + grids.push_back(new T[size.x * size.y * size.z]); + } + if (used > 200) + errMsg("too many temp grids used -- are they released properly ?"); + return grids[used++]; +} +template<class T> +void FluidSolver::GridStorage<T>::release(T* ptr) { + // rewrite pointer, as it may have changed due to swap operations + used--; + if (used < 0) + errMsg("temp grid inconsistency"); + grids[used] = ptr; +} + +template<> int* FluidSolver::getGridPointer<int>() { + return mGridsInt.get(mGridSize); +} +template<> Real* FluidSolver::getGridPointer<Real>() { + return mGridsReal.get(mGridSize); +} +template<> Vec3* FluidSolver::getGridPointer<Vec3>() { + return mGridsVec.get(mGridSize); +} +template<> void FluidSolver::freeGridPointer<int>(int *ptr) { + mGridsInt.release(ptr); +} +template<> void FluidSolver::freeGridPointer<Real>(Real* ptr) { + mGridsReal.release(ptr); +} +template<> void FluidSolver::freeGridPointer<Vec3>(Vec3* ptr) { + mGridsVec.release(ptr); +} + +//****************************************************************************** +// FluidSolver members + +FluidSolver::FluidSolver(Vec3i gridsize, int dim) + : PbClass(this), mDt(1.0), mGridSize(gridsize), mDim(dim), mTimeTotal(0.), mScale(1.0), mFrame(0) +{ + assertMsg(dim==2 || dim==3, "Can only create 2D and 3D solvers"); + assertMsg(dim!=2 || gridsize.z == 1, "Trying to create 2D solver with size.z != 1"); +} + +FluidSolver::~FluidSolver() { + mGridsInt.free(); + mGridsReal.free(); + mGridsVec.free(); +} + +PbClass* FluidSolver::create(PbType t, PbTypeVec T, const string& name) { + _args.add("nocheck",true); + if (t.str() == "") + errMsg("Need to specify object type. Use e.g. Solver.create(FlagGrid, ...) or Solver.create(type=FlagGrid, ...)"); + + return PbClass::createPyObject(t.str() + T.str(), name, _args, this); +} + +void FluidSolver::step() { + mTimeTotal += mDt; + mFrame++; + updateQtGui(true, mFrame, "FluidSolver::step"); +} + +void FluidSolver::printMemInfo() { + std::ostringstream msg; + msg << "Allocated grids: int " << mGridsInt.used <<"/"<< mGridsInt.grids.size() <<", "; + msg << "real "<< mGridsReal.used <<"/"<< mGridsReal.grids.size() <<", "; + msg << "vec3 "<< mGridsVec.used <<"/"<< mGridsVec.grids.size() <<". "; + printf("%s\n", msg.str().c_str() ); +} + +void printBuildInfo() { + debMsg( "Build info: "<<buildInfoString().c_str()<<" ",1); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "printBuildInfo" ); PyObject *_retval = 0; { ArgLocker _lock; _retval = getPyNone(); printBuildInfo(); _args.check(); } pbFinalizePlugin(parent,"printBuildInfo" ); return _retval; } catch(std::exception& e) { pbSetError("printBuildInfo",e.what()); return 0; } } static const Pb::Register _RP_printBuildInfo ("","printBuildInfo",_W_0); + +void setDebugLevel(int level=1) { + gDebugLevel = level; +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setDebugLevel" ); PyObject *_retval = 0; { ArgLocker _lock; int level = _args.getOpt<int >("level",0,1,&_lock); _retval = getPyNone(); setDebugLevel(level); _args.check(); } pbFinalizePlugin(parent,"setDebugLevel" ); return _retval; } catch(std::exception& e) { pbSetError("setDebugLevel",e.what()); return 0; } } static const Pb::Register _RP_setDebugLevel ("","setDebugLevel",_W_1); + +} // manta + + + diff --git a/source/blender/python/manta_pp/fluidsolver.h b/source/blender/python/manta_pp/fluidsolver.h new file mode 100644 index 00000000000..6cea45f2907 --- /dev/null +++ b/source/blender/python/manta_pp/fluidsolver.h @@ -0,0 +1,92 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main class for the fluid solver + * + ******************************************************************************/ + +#ifndef _FLUIDSOLVER_H +#define _FLUIDSOLVER_H + +#include "manta.h" +#include "vectorbase.h" +#include <vector> +#include <map> + +namespace Manta { + +//! Encodes grid size, timstep etc. + +class FluidSolver : public PbClass {public: + FluidSolver(Vec3i gridSize, int dim=3); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "FluidSolver::FluidSolver" ); { ArgLocker _lock; Vec3i gridSize = _args.get<Vec3i >("gridSize",0,&_lock); int dim = _args.getOpt<int >("dim",1,3,&_lock); obj = new FluidSolver(gridSize,dim); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"FluidSolver::FluidSolver" ); return 0; } catch(std::exception& e) { pbSetError("FluidSolver::FluidSolver",e.what()); return -1; } } + virtual ~FluidSolver(); + + // accessors + Vec3i getGridSize() { return mGridSize; } static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FluidSolver::getGridSize"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getGridSize()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FluidSolver::getGridSize"); return _retval; } catch(std::exception& e) { pbSetError("FluidSolver::getGridSize",e.what()); return 0; } } + inline Real getDt() { return mDt; } + inline Real getTime() { return mTimeTotal; } + inline Real getDx() { return 1.0 / mGridSize.max(); } + inline Real getScale() { return mScale; } + //! Check dimensionality + inline bool is2D() const { return mDim==2; } + //! Check dimensionality + inline bool is3D() const { return mDim==3; } + + void printMemInfo(); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FluidSolver::printMemInfo"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->printMemInfo(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FluidSolver::printMemInfo"); return _retval; } catch(std::exception& e) { pbSetError("FluidSolver::printMemInfo",e.what()); return 0; } } + + //! Advance the solver one timestep, update GUI if present + void step(); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FluidSolver::step"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->step(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FluidSolver::step"); return _retval; } catch(std::exception& e) { pbSetError("FluidSolver::step",e.what()); return 0; } } + + //! create a object with the solver as its parent + PbClass* create(PbType type, PbTypeVec T=PbTypeVec(),const std::string& name = ""); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FluidSolver::create"); PyObject *_retval = 0; { ArgLocker _lock; PbType type = _args.get<PbType >("type",0,&_lock); PbTypeVec T = _args.getOpt<PbTypeVec >("T",1,PbTypeVec(),&_lock); const std::string& name = _args.getOpt<std::string >("name",2,"",&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->create(type,T,name)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FluidSolver::create"); return _retval; } catch(std::exception& e) { pbSetError("FluidSolver::create",e.what()); return 0; } } + + // temp grid and plugin stuff: you shouldn't call this manually + template<class T> T* getGridPointer(); + template<class T> void freeGridPointer(T* ptr); + + Real mDt;static PyObject* _GET_mDt(PyObject* self, void* cl) { FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(self)); return toPy(pbo->mDt); } static int _SET_mDt(PyObject* self, PyObject* val, void* cl) { FluidSolver* pbo = dynamic_cast<FluidSolver*>(Pb::objFromPy(self)); pbo->mDt = fromPy<Real >(val); return 0; } +protected: + //! subclass for managing grid memory + //! stored as a stack to allow fast allocation + template<class T> struct GridStorage { + GridStorage() : used(0) {} + T* get(Vec3i size); + void free(); + void release(T* ptr); + + std::vector<T*> grids; + int used; + }; + + Vec3i mGridSize; + const int mDim; + Real mTimeTotal, mScale; + int mFrame; + + GridStorage<int> mGridsInt; + GridStorage<Real> mGridsReal; GridStorage<Vec3> mGridsVec; public: PbArgs _args;} +#define _C_FluidSolver +; + +} + +#endif + + diff --git a/source/blender/python/manta_pp/fluidsolver.h.reg b/source/blender/python/manta_pp/fluidsolver.h.reg new file mode 100644 index 00000000000..29bf91caf59 --- /dev/null +++ b/source/blender/python/manta_pp/fluidsolver.h.reg @@ -0,0 +1,9 @@ +#include "fluidsolver.h" ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","Solver","PbClass"); template<> const char* Namify<FluidSolver >::S = "FluidSolver"; +>FluidSolver^ ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","FluidSolver",FluidSolver::_W_0); ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","getGridSize",FluidSolver::_W_1); ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","printMemInfo",FluidSolver::_W_2); ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","step",FluidSolver::_W_3); ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","create",FluidSolver::_W_4); ++FluidSolver^ static const Pb::Register _R_$IDX$ ("FluidSolver","timestep",FluidSolver::_GET_mDt,FluidSolver::_SET_mDt); diff --git a/source/blender/python/manta_pp/fluidsolver.h.reg.cpp b/source/blender/python/manta_pp/fluidsolver.h.reg.cpp new file mode 100644 index 00000000000..54d8e7fd2fc --- /dev/null +++ b/source/blender/python/manta_pp/fluidsolver.h.reg.cpp @@ -0,0 +1,23 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "fluidsolver.h" +namespace Manta { +#ifdef _C_FluidSolver + static const Pb::Register _R_0 ("FluidSolver","Solver","PbClass"); template<> const char* Namify<FluidSolver >::S = "FluidSolver"; + static const Pb::Register _R_1 ("FluidSolver","FluidSolver",FluidSolver::_W_0); + static const Pb::Register _R_2 ("FluidSolver","getGridSize",FluidSolver::_W_1); + static const Pb::Register _R_3 ("FluidSolver","printMemInfo",FluidSolver::_W_2); + static const Pb::Register _R_4 ("FluidSolver","step",FluidSolver::_W_3); + static const Pb::Register _R_5 ("FluidSolver","create",FluidSolver::_W_4); + static const Pb::Register _R_6 ("FluidSolver","timestep",FluidSolver::_GET_mDt,FluidSolver::_SET_mDt); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/general.cpp b/source/blender/python/manta_pp/general.cpp new file mode 100644 index 00000000000..44f30ee9ef2 --- /dev/null +++ b/source/blender/python/manta_pp/general.cpp @@ -0,0 +1,141 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Globally used macros and functions + * + ******************************************************************************/ + +#include "general.h" +#if defined(WIN32) || defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX +#else +# include <sys/time.h> +# include "hginfo.h" +#endif + +using namespace std; + +namespace Manta { + +int gDebugLevel = 1; + +void MuTime::get() { +#if defined(WIN32) || defined(_WIN32) + LARGE_INTEGER liTimerFrequency; + QueryPerformanceFrequency(&liTimerFrequency); + LARGE_INTEGER liLastTime; + QueryPerformanceCounter(&liLastTime); + time = (INT)( ((double)liLastTime.QuadPart / liTimerFrequency.QuadPart)*1000 ); +#else + struct timeval tv; + struct timezone tz; + tz.tz_minuteswest = 0; + tz.tz_dsttime = 0; + gettimeofday(&tv,&tz); + time = (tv.tv_sec*1000)+(tv.tv_usec/1000); +#endif +} + +MuTime MuTime::update() { + MuTime o = *this; + get(); + return *this - o; +} + +string MuTime::toString() { + stringstream ss; + ss << *this; + return ss.str(); +} + +ostream& operator<<(ostream& os, const MuTime& t) { + unsigned long ms = (unsigned long)( (double)t.time / (60.0*1000.0) ); + unsigned long ss = (unsigned long)( ((double)t.time / 1000.0) - ((double)ms*60.0) ); + int ps = (int)( ((double)t.time - (double)ss*1000.0)/1.0 ); + + if(ms>0) { + os << ms<<"m"<< ss<<"s" ; + } else { + if(ps>0) { + os << ss<<"."; + if(ps<10) { os <<"0"; } + if(ps<100) { os <<"0"; } + os <<ps<<"s" ; + } else { + os << ss<<"s" ; + } + } + return os; +} + +// print info about this mantaflow build, used eg by printBuildInfo in fluidsolver.cpp +std::string buildInfoString() { + std::ostringstream infoStr; + infoStr << "mantaflow"; + + // os +#if defined(WIN32) || defined(_WIN32) + infoStr << " win"; +# endif +# ifdef __APPLE__ + infoStr << " mac"; +# endif +# ifdef LINUX + infoStr << " linux"; +# endif + + // 32/64 bit + if (sizeof(size_t) == 8) + infoStr << " 64bit"; + else + infoStr << " 32bit"; + + // fp precision +# if FLOATINGPOINT_PRECISION==2 + infoStr << " fp2"; +# else + infoStr << " fp1"; +# endif + + // other compile switches +# ifdef DEBUG + infoStr << " debug"; +# endif +# ifdef OPENMP + infoStr << " omp"; +# endif + + // repository info (git commit id) +# ifndef MANTA_HG_VERSION +# define MANTA_HG_VERSION "<unknown-commit>" +# endif + infoStr << " "<< MANTA_HG_VERSION; + + infoStr << " from "<< __DATE__<<", "<<__TIME__; + return infoStr.str(); +} + +} // namespace + + diff --git a/source/blender/python/manta_pp/general.h b/source/blender/python/manta_pp/general.h new file mode 100644 index 00000000000..45958d73324 --- /dev/null +++ b/source/blender/python/manta_pp/general.h @@ -0,0 +1,151 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Globally used macros and functions + * + ******************************************************************************/ + +#ifndef _GENERAL_H +#define _GENERAL_H + +#include <iostream> +#include <sstream> +#include <cmath> + +namespace Manta { + +// activate debug mode if _DEBUG is defined (eg for windows) +#ifndef DEBUG +#ifdef _DEBUG +#define DEBUG 1 +#endif // _DEBUG +#endif // DEBUG + +// Standard exception +class Error : public std::exception +{ +public: + Error(const std::string& s) : mS(s) { +# ifdef DEBUG + // print error + std::cerr << "Aborting: "<< s <<" \n"; + // then force immedieate crash in debug mode + *(volatile int*)(0) = 1; +# endif + } + virtual ~Error() throw() {} + virtual const char* what() const throw() { return mS.c_str(); } +private: + std::string mS; +}; + +// mark unused parameter variables +#define unusedParameter(x) ((void)x) + +// Debug output functions and macros +extern int gDebugLevel; + +#define MSGSTREAM std::ostringstream msg; msg.precision(7); msg.width(9); +#define debMsg(mStr, level) if (_chklevel(level)) { MSGSTREAM; msg << mStr; std::cout << msg.str() << std::endl; } +inline bool _chklevel(int level=0) { return gDebugLevel >= level; } + +// error and assertation macros +#ifdef DEBUG +# define DEBUG_ONLY(a) a +#else +# define DEBUG_ONLY(a) +#endif +#define throwError(msg) { std::ostringstream __s; __s << msg << std::endl << "Error raised in " << __FILE__ << ":" << __LINE__; throw Manta::Error(__s.str()); } +#define errMsg(msg) throwError(msg); +#define assertMsg(cond,msg) if(!(cond)) throwError(msg) +#define assertDeb(cond,msg) DEBUG_ONLY( assertMsg(cond,msg) ) + +// template tricks +template<typename T> +struct remove_pointers { + typedef T type; +}; + +template<typename T> +struct remove_pointers<T*> { + typedef T type; +}; + +template<typename T> +struct remove_pointers<T&> { + typedef T type; +}; + +// Commonly used enums and types +//! Timing class for preformance measuring +struct MuTime { + MuTime() { get(); } + MuTime operator-(const MuTime& a) { MuTime b; b.time = time - a.time; return b; }; + MuTime operator+(const MuTime& a) { MuTime b; b.time = time + a.time; return b; }; + MuTime operator/(unsigned long a) { MuTime b; b.time = time / a; return b; }; + MuTime& operator+=(const MuTime& a) { time += a.time; return *this; } + MuTime& operator-=(const MuTime& a) { time -= a.time; return *this; } + MuTime& operator/=(unsigned long a) { time /= a; return *this; } + std::string toString(); + + void clear() { time = 0; } + void get(); + MuTime update(); + + unsigned long time; +}; +std::ostream& operator<< (std::ostream& os, const MuTime& t); + +//! generate a string with infos about the current mantaflow build +std::string buildInfoString(); + +// Some commonly used math helpers +template<class T> inline T square(T a) { + return a*a; +} +template<class T> inline T cubed(T a) { + return a*a; +} + +template<class T> inline T clamp(const T& val, const T& vmin, const T& vmax) { + if (val < vmin) return vmin; + if (val > vmax) return vmax; + return val; +} + +template<class T> inline T nmod(const T& a, const T& b); +template<> inline int nmod(const int& a, const int& b) { int c=a%b; return (c<0) ? (c+b) : c; } +template<> inline float nmod(const float& a, const float& b) { float c=std::fmod(a,b); return (c<0) ? (c+b) : c; } +template<> inline double nmod(const double& a, const double& b) { double c=std::fmod(a,b); return (c<0) ? (c+b) : c; } +template<class T> inline T safeDivide(const T& a, const T& b); +template<> inline int safeDivide<int>(const int &a, const int& b) { return (b) ? (a/b) : a; } +template<> inline float safeDivide<float>(const float &a, const float& b) { return (b) ? (a/b) : a; } +template<> inline double safeDivide<double>(const double &a, const double& b) { return (b) ? (a/b) : a; } + +inline bool c_isnan(float c) { + volatile float d=c; + return d != d; +} + +} // namespace + +#endif + + diff --git a/source/blender/python/manta_pp/general.h.reg b/source/blender/python/manta_pp/general.h.reg new file mode 100644 index 00000000000..65a276295d5 --- /dev/null +++ b/source/blender/python/manta_pp/general.h.reg @@ -0,0 +1 @@ +#include "general.h" diff --git a/source/blender/python/manta_pp/general.h.reg.cpp b/source/blender/python/manta_pp/general.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/general.h.reg.cpp diff --git a/source/blender/python/manta_pp/grid.cpp b/source/blender/python/manta_pp/grid.cpp new file mode 100644 index 00000000000..d47496623f4 --- /dev/null +++ b/source/blender/python/manta_pp/grid.cpp @@ -0,0 +1,560 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Grid representation + * + ******************************************************************************/ + +#include "grid.h" +#include "levelset.h" +#include "kernel.h" +#include <limits> +#include <sstream> +#include <cstring> +#include "fileio.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// GridBase members + +GridBase::GridBase (FluidSolver* parent) + : PbClass(parent), mType(TypeNone) +{ + checkParent(); + m3D = getParent()->is3D(); +} + +//****************************************************************************** +// Grid<T> members + +// helpers to set type +template<class T> inline GridBase::GridType typeList() { return GridBase::TypeNone; } +template<> inline GridBase::GridType typeList<Real>() { return GridBase::TypeReal; } +template<> inline GridBase::GridType typeList<int>() { return GridBase::TypeInt; } +template<> inline GridBase::GridType typeList<Vec3>() { return GridBase::TypeVec3; } + +template<class T> +Grid<T>::Grid(FluidSolver* parent, bool show) + : GridBase(parent) +{ + mType = typeList<T>(); + mSize = parent->getGridSize(); + mData = parent->getGridPointer<T>(); + + mStrideZ = parent->is2D() ? 0 : (mSize.x * mSize.y); + mDx = 1.0 / mSize.max(); + clear(); + setHidden(!show); +} + +template<class T> +Grid<T>::Grid(const Grid<T>& a) : GridBase(a.getParent()) { + mSize = a.mSize; + mType = a.mType; + mStrideZ = a.mStrideZ; + mDx = a.mDx; + FluidSolver *gp = a.getParent(); + mData = gp->getGridPointer<T>(); + memcpy(mData, a.mData, sizeof(T) * a.mSize.x * a.mSize.y * a.mSize.z); +} + +template<class T> +Grid<T>::~Grid() { + mParent->freeGridPointer<T>(mData); +} + +template<class T> +void Grid<T>::clear() { + memset(mData, 0, sizeof(T) * mSize.x * mSize.y * mSize.z); +} + +template<class T> +void Grid<T>::swap(Grid<T>& other) { + if (other.getSizeX() != getSizeX() || other.getSizeY() != getSizeY() || other.getSizeZ() != getSizeZ()) + errMsg("Grid::swap(): Grid dimensions mismatch."); + + T* dswap = other.mData; + other.mData = mData; + mData = dswap; +} + +template<class T> +void Grid<T>::load(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".raw") + readGridRaw(name, this); + else if (ext == ".uni") + readGridUni(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +template<class T> +void Grid<T>::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".raw") + writeGridRaw(name, this); + else if (ext == ".uni") + writeGridUni(name, this); + else if (ext == ".vol") + writeGridVol(name, this); + else if (ext == ".txt") + writeGridTxt(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +template<class T> +void Grid<T>::loadIncrement(string name) { + Grid<T> temp(*this); + temp.load(name); + for(int i=0; i< mSize[0] * mSize[1] * mSize[2]; ++i){ + mData[i] += temp[i]; + } +} +//****************************************************************************** +// Grid<T> operators + +//! Kernel: Compute min value of Real grid + + struct CompMinReal : public KernelBase { CompMinReal(Grid<Real>& val) : KernelBase(&val,0) ,val(val) ,minVal(std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, Grid<Real>& val ,Real& minVal) { + if (val[idx] < minVal) + minVal = val[idx]; +} inline operator Real () { return minVal; } inline Real & getRet() { return minVal; } inline Grid<Real>& getArg0() { return val; } typedef Grid<Real> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,minVal); } Grid<Real>& val; Real minVal; }; + +//! Kernel: Compute max value of Real grid + + struct CompMaxReal : public KernelBase { CompMaxReal(Grid<Real>& val) : KernelBase(&val,0) ,val(val) ,maxVal(-std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, Grid<Real>& val ,Real& maxVal) { + if (val[idx] > maxVal) + maxVal = val[idx]; +} inline operator Real () { return maxVal; } inline Real & getRet() { return maxVal; } inline Grid<Real>& getArg0() { return val; } typedef Grid<Real> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,maxVal); } Grid<Real>& val; Real maxVal; }; + +//! Kernel: Compute min value of int grid + + struct CompMinInt : public KernelBase { CompMinInt(Grid<int>& val) : KernelBase(&val,0) ,val(val) ,minVal(std::numeric_limits<int>::max()) { run(); } inline void op(int idx, Grid<int>& val ,int& minVal) { + if (val[idx] < minVal) + minVal = val[idx]; +} inline operator int () { return minVal; } inline int & getRet() { return minVal; } inline Grid<int>& getArg0() { return val; } typedef Grid<int> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,minVal); } Grid<int>& val; int minVal; }; + +//! Kernel: Compute max value of int grid + + struct CompMaxInt : public KernelBase { CompMaxInt(Grid<int>& val) : KernelBase(&val,0) ,val(val) ,maxVal(std::numeric_limits<int>::min()) { run(); } inline void op(int idx, Grid<int>& val ,int& maxVal) { + if (val[idx] > maxVal) + maxVal = val[idx]; +} inline operator int () { return maxVal; } inline int & getRet() { return maxVal; } inline Grid<int>& getArg0() { return val; } typedef Grid<int> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,maxVal); } Grid<int>& val; int maxVal; }; + +//! Kernel: Compute min norm of vec grid + + struct CompMinVec : public KernelBase { CompMinVec(Grid<Vec3>& val) : KernelBase(&val,0) ,val(val) ,minVal(std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, Grid<Vec3>& val ,Real& minVal) { + const Real s = normSquare(val[idx]); + if (s < minVal) + minVal = s; +} inline operator Real () { return minVal; } inline Real & getRet() { return minVal; } inline Grid<Vec3>& getArg0() { return val; } typedef Grid<Vec3> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,minVal); } Grid<Vec3>& val; Real minVal; }; + +//! Kernel: Compute max norm of vec grid + + struct CompMaxVec : public KernelBase { CompMaxVec(Grid<Vec3>& val) : KernelBase(&val,0) ,val(val) ,maxVal(-std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, Grid<Vec3>& val ,Real& maxVal) { + const Real s = normSquare(val[idx]); + if (s > maxVal) + maxVal = s; +} inline operator Real () { return maxVal; } inline Real & getRet() { return maxVal; } inline Grid<Vec3>& getArg0() { return val; } typedef Grid<Vec3> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,maxVal); } Grid<Vec3>& val; Real maxVal; }; + + +template<class T> Grid<T>& Grid<T>::safeDivide (const Grid<T>& a) { + gridSafeDiv<T> (*this, a); + return *this; +} +template<class T> Grid<T>& Grid<T>::copyFrom (const Grid<T>& a) { + assertMsg (a.mSize.x == mSize.x && a.mSize.y == mSize.y && a.mSize.z == mSize.z, "different grid resolutions "<<a.mSize<<" vs "<<this->mSize ); + memcpy(mData, a.mData, sizeof(T) * mSize.x * mSize.y * mSize.z); + mType = a.mType; // copy type marker + return *this; +} +/*template<class T> Grid<T>& Grid<T>::operator= (const Grid<T>& a) { + note: do not use , use copyFrom instead +}*/ + +template <class T> struct knGridSetConstReal : public KernelBase { knGridSetConstReal(Grid<T>& me, T val) : KernelBase(&me,0) ,me(me),val(val) { run(); } inline void op(int idx, Grid<T>& me, T val ) { me[idx] = val; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline T& getArg1() { return val; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,val); } Grid<T>& me; T val; }; +template <class T> struct knGridAddConstReal : public KernelBase { knGridAddConstReal(Grid<T>& me, T val) : KernelBase(&me,0) ,me(me),val(val) { run(); } inline void op(int idx, Grid<T>& me, T val ) { me[idx] += val; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline T& getArg1() { return val; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,val); } Grid<T>& me; T val; }; +template <class T> struct knGridMultConst : public KernelBase { knGridMultConst(Grid<T>& me, T val) : KernelBase(&me,0) ,me(me),val(val) { run(); } inline void op(int idx, Grid<T>& me, T val ) { me[idx] *= val; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline T& getArg1() { return val; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,val); } Grid<T>& me; T val; }; +template <class T> struct knGridClamp : public KernelBase { knGridClamp(Grid<T>& me, T min, T max) : KernelBase(&me,0) ,me(me),min(min),max(max) { run(); } inline void op(int idx, Grid<T>& me, T min, T max ) { me[idx] = clamp( me[idx], min, max); } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline T& getArg1() { return min; } typedef T type1;inline T& getArg2() { return max; } typedef T type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,min,max); } Grid<T>& me; T min; T max; }; + +template<class T> void Grid<T>::add(const Grid<T>& a) { + gridAdd<T,T>(*this, a); +} +template<class T> void Grid<T>::sub(const Grid<T>& a) { + gridSub<T,T>(*this, a); +} +template<class T> void Grid<T>::addScaled(const Grid<T>& a, const T& factor) { + gridScaledAdd<T,T> (*this, a, factor); +} +template<class T> void Grid<T>::setConst(T a) { + knGridSetConstReal<T>( *this, T(a) ); +} +template<class T> void Grid<T>::addConst(T a) { + knGridAddConstReal<T>( *this, T(a) ); +} +template<class T> void Grid<T>::multConst(T a) { + knGridMultConst<T>( *this, a ); +} + +template<class T> void Grid<T>::mult(const Grid<T>& a) { + gridMult<T,T> (*this, a); +} + +template<class T> void Grid<T>::clamp(Real min, Real max) { + knGridClamp<T> (*this, T(min), T(max) ); +} + +template<> Real Grid<Real>::getMaxValue() { + return CompMaxReal (*this); +} +template<> Real Grid<Real>::getMinValue() { + return CompMinReal (*this); +} +template<> Real Grid<Real>::getMaxAbsValue() { + Real amin = CompMinReal (*this); + Real amax = CompMaxReal (*this); + return max( fabs(amin), fabs(amax)); +} +template<> Real Grid<Vec3>::getMaxValue() { + return sqrt(CompMaxVec (*this)); +} +template<> Real Grid<Vec3>::getMinValue() { + return sqrt(CompMinVec (*this)); +} +template<> Real Grid<Vec3>::getMaxAbsValue() { + return sqrt(CompMaxVec (*this)); +} +template<> Real Grid<int>::getMaxValue() { + return (Real) CompMaxInt (*this); +} +template<> Real Grid<int>::getMinValue() { + return (Real) CompMinInt (*this); +} +template<> Real Grid<int>::getMaxAbsValue() { + int amin = CompMinInt (*this); + int amax = CompMaxInt (*this); + return max( fabs((Real)amin), fabs((Real)amax)); +} + +// compute maximal diference of two cells in the grid +// used for testing system + +Real gridMaxDiff(Grid<Real>& g1, Grid<Real>& g2 ) { + double maxVal = 0.; + FOR_IJK(g1) { + maxVal = std::max(maxVal, (double)fabs( g1(i,j,k)-g2(i,j,k) )); + } + return maxVal; +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "gridMaxDiff" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& g1 = *_args.getPtr<Grid<Real> >("g1",0,&_lock); Grid<Real>& g2 = *_args.getPtr<Grid<Real> >("g2",1,&_lock); _retval = toPy(gridMaxDiff(g1,g2)); _args.check(); } pbFinalizePlugin(parent,"gridMaxDiff" ); return _retval; } catch(std::exception& e) { pbSetError("gridMaxDiff",e.what()); return 0; } } static const Pb::Register _RP_gridMaxDiff ("","gridMaxDiff",_W_0); + +Real gridMaxDiffInt(Grid<int>& g1, Grid<int>& g2 ) { + double maxVal = 0.; + FOR_IJK(g1) { + maxVal = std::max(maxVal, (double)fabs( (double)g1(i,j,k)-g2(i,j,k) )); + } + return maxVal; +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "gridMaxDiffInt" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<int>& g1 = *_args.getPtr<Grid<int> >("g1",0,&_lock); Grid<int>& g2 = *_args.getPtr<Grid<int> >("g2",1,&_lock); _retval = toPy(gridMaxDiffInt(g1,g2)); _args.check(); } pbFinalizePlugin(parent,"gridMaxDiffInt" ); return _retval; } catch(std::exception& e) { pbSetError("gridMaxDiffInt",e.what()); return 0; } } static const Pb::Register _RP_gridMaxDiffInt ("","gridMaxDiffInt",_W_1); + +Real gridMaxDiffVec3(Grid<Vec3>& g1, Grid<Vec3>& g2 ) { + double maxVal = 0.; + FOR_IJK(g1) { + // accumulate differences with double precision + // note - don't use norm here! should be as precise as possible... + double d = 0.; + for(int c=0; c<3; ++c) { + d += fabs( (double)g1(i,j,k)[c] - (double)g2(i,j,k)[c] ); + } + maxVal = std::max(maxVal, d ); + //maxVal = std::max(maxVal, (double)fabs( norm(g1(i,j,k)-g2(i,j,k)) )); + } + return maxVal; +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "gridMaxDiffVec3" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Vec3>& g1 = *_args.getPtr<Grid<Vec3> >("g1",0,&_lock); Grid<Vec3>& g2 = *_args.getPtr<Grid<Vec3> >("g2",1,&_lock); _retval = toPy(gridMaxDiffVec3(g1,g2)); _args.check(); } pbFinalizePlugin(parent,"gridMaxDiffVec3" ); return _retval; } catch(std::exception& e) { pbSetError("gridMaxDiffVec3",e.what()); return 0; } } static const Pb::Register _RP_gridMaxDiffVec3 ("","gridMaxDiffVec3",_W_2); + +// simple helper functions to convert mac to vec3 , and levelset to real grids +// (are assumed to be the same for running the test cases - in general they're not!) + +void convertMacToVec3(MACGrid &source, Grid<Vec3>& target) { + FOR_IJK(target) { + target(i,j,k) = source(i,j,k); + } +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "convertMacToVec3" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& source = *_args.getPtr<MACGrid >("source",0,&_lock); Grid<Vec3>& target = *_args.getPtr<Grid<Vec3> >("target",1,&_lock); _retval = getPyNone(); convertMacToVec3(source,target); _args.check(); } pbFinalizePlugin(parent,"convertMacToVec3" ); return _retval; } catch(std::exception& e) { pbSetError("convertMacToVec3",e.what()); return 0; } } static const Pb::Register _RP_convertMacToVec3 ("","convertMacToVec3",_W_3); + + +void convertLevelsetToReal(LevelsetGrid &source , Grid<Real> &target) { + FOR_IJK(target) { + target(i,j,k) = source(i,j,k); + } +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "convertLevelsetToReal" ); PyObject *_retval = 0; { ArgLocker _lock; LevelsetGrid& source = *_args.getPtr<LevelsetGrid >("source",0,&_lock); Grid<Real> & target = *_args.getPtr<Grid<Real> >("target",1,&_lock); _retval = getPyNone(); convertLevelsetToReal(source,target); _args.check(); } pbFinalizePlugin(parent,"convertLevelsetToReal" ); return _retval; } catch(std::exception& e) { pbSetError("convertLevelsetToReal",e.what()); return 0; } } static const Pb::Register _RP_convertLevelsetToReal ("","convertLevelsetToReal",_W_4); + + +template<class T> void Grid<T>::printGrid(int zSlice, bool printIndex) { + std::ostringstream out; + out << std::endl; + const int bnd = 1; + FOR_IJK_BND(*this,bnd) { + int idx = (*this).index(i,j,k); + if(zSlice>=0 && k==zSlice) { + out << " "; + if(printIndex) out << " "<<i<<","<<j<<","<<k <<":"; + out << (*this)[idx]; + if(i==(*this).getSizeX()-1 -bnd) out << std::endl; + } + } + out << endl; debMsg("Printing " << this->getName() << out.str().c_str() , 1); +} + + +template<class T> void Grid<T>::writeGridToMemory(const std::string& memLoc, const std::string& sizeAllowed) +{ + if (memLoc == "" ||memLoc == "0" ){ + debMsg("Cant write grid to NULL pointer",1); + return; + } + istringstream iss(sizeAllowed); + size_t sizeAllowed_num; + iss >> sizeAllowed_num; + if (sizeof(T) * mSize.x * mSize.y * mSize.z != sizeAllowed_num){ + debMsg("Cant write grid with incompatible size",1); + return; + } + stringstream ss(memLoc); + void *gridPointer = NULL; + ss >> gridPointer; + memcpy(gridPointer, mData, sizeAllowed_num); +} + +template<class T> void Grid<T>::readGridFromMemory(const std::string& memLoc, int x, int y, int z) +{ + debMsg("Reading grid from " + memLoc,1); + if (memLoc == "" ||memLoc == "0" ){ + debMsg("Can not write grid to NULL pointer",1); + return; + } + if (x != mSize.x || y != mSize.y || z != mSize.z) + { + debMsg("Can not write grid with different domain size",1); + return; + } + stringstream ss(memLoc); + void *gridPointer = NULL; + ss >> gridPointer; + memcpy(mData, gridPointer, sizeof(T) * x * y * z); +} + +template<class T> void Grid<T>::readAdaptiveGridFromMemory(const std::string& memLoc, Vec3i minSize, Vec3i maxSize) +{ + if (memLoc == "" ||memLoc == "0" ){ + debMsg("Can not write grid to NULL pointer",1); + return; + } + if (minSize.x < 0 || minSize.y < 0 || minSize.z < 0){ + debMsg("Adaptive grid smaller than 0",1); + return; + } + if (maxSize.x > mSize.x || maxSize.y > mSize.y || maxSize.z > mSize.z){ + debMsg("Adaptive grid larger than current",1); + return; + } + Vec3i adaptiveSize = maxSize - minSize; + stringstream ss(memLoc); + void *gridPointer = NULL; + ss >> gridPointer; + float *data_Array = (float* )gridPointer; + for (int x = 0; x < adaptiveSize.x; ++x){ + for (int y = 0; y < adaptiveSize.y; ++y){ + for (int z = 0; z < adaptiveSize.z; ++z){ + get(x + minSize.x, y + minSize.y, z + minSize.z) = data_Array[x + adaptiveSize.x * y + adaptiveSize.x * adaptiveSize.y * z]; + } + } + } +} + +template<class T> std::string Grid<T>::getDataPointer() +{ + ostringstream ss; + ss << mData ; + return ss.str(); +} + +//! Kernel: Apply a texture to a grid, setting texture(ijk)*value where texture(ijk) > 0 +//note: can not use template kernel here, because of Blender classes + + struct ApplyTextureToRealGrid : public KernelBase { ApplyTextureToRealGrid(Grid<Real> *grid, Grid<Real> *texture, Real value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),texture(texture),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, Grid<Real> *grid, Grid<Real> *texture, Real value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if ((*texture)(i,j,k) > 0) + (*grid)(i,j,k) = (*texture)(i,j,k) * value; +} inline Grid<Real> * getArg0() { return grid; } typedef Grid<Real> type0;inline Grid<Real> * getArg1() { return texture; } typedef Grid<Real> type1;inline Real& getArg2() { return value; } typedef Real type2;inline FlagGrid* getArg3() { return respectFlags; } typedef FlagGrid type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,texture,value,respectFlags); } Grid<Real> * grid; Grid<Real> * texture; Real value; FlagGrid* respectFlags; }; + + + struct ApplyTextureToIntGrid : public KernelBase { ApplyTextureToIntGrid(Grid<int> *grid, Grid<Real> *texture, int value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),texture(texture),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, Grid<int> *grid, Grid<Real> *texture, int value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if ((*texture)(i,j,k) > 0) + (*grid)(i,j,k) = value ; +} inline Grid<int> * getArg0() { return grid; } typedef Grid<int> type0;inline Grid<Real> * getArg1() { return texture; } typedef Grid<Real> type1;inline int& getArg2() { return value; } typedef int type2;inline FlagGrid* getArg3() { return respectFlags; } typedef FlagGrid type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,texture,value,respectFlags); } Grid<int> * grid; Grid<Real> * texture; int value; FlagGrid* respectFlags; }; + +template<class T> void Grid<T>::applyToGrid(GridBase *grid, FlagGrid* respectFlags) +{ + if (this->getType() & GridBase::TypeReal) + ApplyTextureToRealGrid((Grid<Real>*)grid, (Grid<Real>*)this, _args.get<Real>("value"), respectFlags); + if (this->getType() & GridBase::TypeInt) + ApplyTextureToIntGrid((Grid<int>*)grid, (Grid<Real>*)this, _args.get<int>("value"), respectFlags); +} + +// helper functions for UV grid data (stored grid coordinates as Vec3 values, and uv weight in entry zero) + +// make uv weight accesible in python +Real getUvWeight(Grid<Vec3> &uv) { return uv[0][0]; } static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "getUvWeight" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Vec3> & uv = *_args.getPtr<Grid<Vec3> >("uv",0,&_lock); _retval = toPy(getUvWeight(uv)); _args.check(); } pbFinalizePlugin(parent,"getUvWeight" ); return _retval; } catch(std::exception& e) { pbSetError("getUvWeight",e.what()); return 0; } } static const Pb::Register _RP_getUvWeight ("","getUvWeight",_W_5); + +// note - right now the UV grids have 0 values at the border after advection... could be fixed with an extrapolation step... + +// compute normalized modulo interval +static inline Real computeUvGridTime(Real t, Real resetTime) { + return fmod( (t / resetTime), (Real)1. ); +} +// create ramp function in 0..1 range with half frequency +static inline Real computeUvRamp(Real t) { + Real uvWeight = 2. * t; + if (uvWeight>1.) uvWeight=2.-uvWeight; + return uvWeight; +} + + struct knResetUvGrid : public KernelBase { knResetUvGrid(Grid<Vec3>& target) : KernelBase(&target,0) ,target(target) { run(); } inline void op(int i, int j, int k, Grid<Vec3>& target ) { target(i,j,k) = Vec3((Real)i,(Real)j,(Real)k); } inline Grid<Vec3>& getArg0() { return target; } typedef Grid<Vec3> type0; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, target); } Grid<Vec3>& target; }; + + +void resetUvGrid(Grid<Vec3> &target) { + knResetUvGrid reset(target); // note, llvm complains about anonymous declaration here... ? +} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "resetUvGrid" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Vec3> & target = *_args.getPtr<Grid<Vec3> >("target",0,&_lock); _retval = getPyNone(); resetUvGrid(target); _args.check(); } pbFinalizePlugin(parent,"resetUvGrid" ); return _retval; } catch(std::exception& e) { pbSetError("resetUvGrid",e.what()); return 0; } } static const Pb::Register _RP_resetUvGrid ("","resetUvGrid",_W_6); + +void updateUvWeight(Real resetTime, int index, int numUvs, Grid<Vec3> &uv , bool info=false) { + const Real t = uv.getParent()->getTime(); + Real timeOff = resetTime/(Real)numUvs; + + Real lastt = computeUvGridTime(t +(Real)index*timeOff - uv.getParent()->getDt(), resetTime); + Real currt = computeUvGridTime(t +(Real)index*timeOff , resetTime); + Real uvWeight = computeUvRamp(currt); + + // normalize the uvw weights , note: this is a bit wasteful... + Real uvWTotal = 0.; + for(int i=0; i<numUvs; ++i) { + uvWTotal += computeUvRamp( computeUvGridTime(t +(Real)i*timeOff , resetTime) ); + } + if(uvWTotal<=VECTOR_EPSILON) { uvWeight = uvWTotal = 1.; } + else uvWeight /= uvWTotal; + + // check for reset + if( currt < lastt ) + knResetUvGrid reset( uv ); + + // write new weight value to grid + uv[0] = Vec3( uvWeight, 0.,0.); + + // print info about uv weights? + if(info) debMsg("Uv grid "<<index<<"/"<<numUvs<< " t="<<currt<<" w="<<uvWeight<<", reset:"<<(int)(currt<lastt) , 1); +} static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "updateUvWeight" ); PyObject *_retval = 0; { ArgLocker _lock; Real resetTime = _args.get<Real >("resetTime",0,&_lock); int index = _args.get<int >("index",1,&_lock); int numUvs = _args.get<int >("numUvs",2,&_lock); Grid<Vec3> & uv = *_args.getPtr<Grid<Vec3> >("uv",3,&_lock); bool info = _args.getOpt<bool >("info",4,false,&_lock); _retval = getPyNone(); updateUvWeight(resetTime,index,numUvs,uv,info); _args.check(); } pbFinalizePlugin(parent,"updateUvWeight" ); return _retval; } catch(std::exception& e) { pbSetError("updateUvWeight",e.what()); return 0; } } static const Pb::Register _RP_updateUvWeight ("","updateUvWeight",_W_7); + +void setBoundaries(Grid<Real>& grid, Real value=0., int boundaryWidth=1) { + const int w = boundaryWidth; + FOR_IJK(grid) { + bool bnd = (i<=w || i>=grid.getSizeX()-1-w || j<=w || j>=grid.getSizeY()-1-w || (grid.is3D() && (k<=w || k>=grid.getSizeZ()-1-w))); + if (bnd) + grid(i,j,k) = value; + } +} static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setBoundaries" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& grid = *_args.getPtr<Grid<Real> >("grid",0,&_lock); Real value = _args.getOpt<Real >("value",1,0.,&_lock); int boundaryWidth = _args.getOpt<int >("boundaryWidth",2,1,&_lock); _retval = getPyNone(); setBoundaries(grid,value,boundaryWidth); _args.check(); } pbFinalizePlugin(parent,"setBoundaries" ); return _retval; } catch(std::exception& e) { pbSetError("setBoundaries",e.what()); return 0; } } static const Pb::Register _RP_setBoundaries ("","setBoundaries",_W_8); + +//****************************************************************************** +// Specialization classes + +void FlagGrid::initDomain(int boundaryWidth) { + FOR_IDX(*this) + mData[idx] = TypeEmpty; + initBoundaries(boundaryWidth); + // MLE 2014-06-25 + bWidth = boundaryWidth; +} + +void FlagGrid::initBoundaries(int boundaryWidth) { + const int w = boundaryWidth; + FOR_IJK(*this) { + bool bnd = (i<=w || i>=mSize.x-1-w || j<=w || j>=mSize.y-1-w || (is3D() && (k<=w || k>=mSize.z-1-w))); + if (bnd) + mData[index(i,j,k)] = TypeObstacle; + } +} + +void FlagGrid::updateFromLevelset(LevelsetGrid& levelset) { + FOR_IDX(*this) { + if (!isObstacle(idx)) { + const Real phi = levelset[idx]; + if (phi <= levelset.invalidTimeValue()) continue; + + mData[idx] &= ~(TypeEmpty | TypeFluid); // clear empty/fluid flags + mData[idx] |= (phi <= 0) ? TypeFluid : TypeEmpty; // set resepctive flag + } + } +} + +void FlagGrid::fillGrid(int type) { + FOR_IDX(*this) { + if ((mData[idx] & TypeObstacle)==0) + mData[idx] = (mData[idx] & ~(TypeEmpty | TypeFluid)) | type; + } +} + +// explicit instantiation +template class Grid<int>; +template class Grid<Real>; +template class Grid<Vec3>; + + +//****************************************************************************** +// enable compilation of a more complicated test data type +// enable in grid.h + +#if ENABLE_GRID_TEST_DATATYPE==1 +// NT_DEBUG ? template<> const char* Namify<nbVector>::S = "TestDatatype"; + +template<> Real Grid<nbVector>::getMinValue() { return 0.; } +template<> Real Grid<nbVector>::getMaxAbsValue() { return 0.; } +template<> Real Grid<nbVector>::getMaxValue() { return 0.; } + + struct knNbvecTestKernel : public KernelBase { knNbvecTestKernel(Grid<nbVector>& target) : KernelBase(&target,0) ,target(target) { run(); } inline void op(int i, int j, int k, Grid<nbVector>& target ) { target(i,j,k).push_back(i+j+k); } inline Grid<nbVector>& getArg0() { return target; } typedef Grid<nbVector> type0; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, target); } Grid<nbVector>& target; }; + +void nbvecTestOp(Grid<nbVector> &target) { + knNbvecTestKernel nbvecTest(target); +} static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "nbvecTestOp" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<nbVector> & target = *_args.getPtr<Grid<nbVector> >("target",0,&_lock); _retval = getPyNone(); nbvecTestOp(target); _args.check(); } pbFinalizePlugin(parent,"nbvecTestOp" ); return _retval; } catch(std::exception& e) { pbSetError("nbvecTestOp",e.what()); return 0; } } static const Pb::Register _RP_nbvecTestOp ("","nbvecTestOp",_W_9); + +// instantiate test datatype , not really required for simulations, mostly here for demonstration purposes +template class Grid<nbVector>; +#endif // ENABLE_GRID_TEST_DATATYPE + + +} //namespace + + diff --git a/source/blender/python/manta_pp/grid.h b/source/blender/python/manta_pp/grid.h new file mode 100644 index 00000000000..3bc5326f3f7 --- /dev/null +++ b/source/blender/python/manta_pp/grid.h @@ -0,0 +1,508 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Grid representation + * + ******************************************************************************/ + +#ifndef _GRID_H +#define _GRID_H + +#include "manta.h" +#include "vectorbase.h" +#include "interpol.h" +#include "interpolHigh.h" +#include "kernel.h" + +namespace Manta { +class LevelsetGrid; +class FlagGrid; + +//! Base class for all grids +class GridBase : public PbClass {public: + enum GridType { TypeNone = 0, TypeReal = 1, TypeInt = 2, TypeVec3 = 4, TypeMAC = 8, TypeLevelset = 16, TypeFlags = 32 }; + + GridBase(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "GridBase::GridBase" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new GridBase(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"GridBase::GridBase" ); return 0; } catch(std::exception& e) { pbSetError("GridBase::GridBase",e.what()); return -1; } } + + //! Get the grids X dimension + inline int getSizeX() const { return mSize.x; } + //! Get the grids Y dimension + inline int getSizeY() const { return mSize.y; } + //! Get the grids Z dimension + inline int getSizeZ() const { return mSize.z; } + //! Get the grids dimensions + inline Vec3i getSize() const { return mSize; } + + //! Get Stride in X dimension + inline int getStrideX() const { return 1; } + //! Get Stride in Y dimension + inline int getStrideY() const { return mSize.x; } + //! Get Stride in Z dimension + inline int getStrideZ() const { return mStrideZ; } + + inline Real getDx() { return mDx; } + + //! Check if indices are within bounds, otherwise error (should only be called when debugging) + inline void checkIndex(int i, int j, int k) const; + //! Check if indices are within bounds, otherwise error (should only be called when debugging) + inline void checkIndex(int idx) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3i& p, int bnd) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3i& p) const; + //! Check if index is within given boundaries + inline bool isInBounds(const Vec3& p, int bnd = 0) const { return isInBounds(toVec3i(p), bnd); } + //! Check if linear index is in the range of the array + inline bool isInBounds(int idx) const; + + //! Get the type of grid + inline GridType getType() const { return mType; } + //! Check dimensionality + inline bool is2D() const { return !m3D; } + //! Check dimensionality + inline bool is3D() const { return m3D; } + + //! Get index into the data + inline int index(int i, int j, int k) const { DEBUG_ONLY(checkIndex(i,j,k)); return i + mSize.x * j + mStrideZ * k; } + //! Get index into the data + inline int index(const Vec3i& pos) const { DEBUG_ONLY(checkIndex(pos.x,pos.y,pos.z)); return pos.x + mSize.x * pos.y + mStrideZ * pos.z; } +protected: + + GridType mType; + Vec3i mSize; + Real mDx; + bool m3D; // precomputed Z shift: to ensure 2D compatibility, always use this instead of sx*sy ! + int mStrideZ; public: PbArgs _args;} +#define _C_GridBase +; + +//! Grid class + +template<class T> class Grid : public GridBase {public: + //! init new grid, values are set to zero + Grid(FluidSolver* parent, bool show = true); static int _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Grid::Grid" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); bool show = _args.getOpt<bool >("show",1,true,&_lock); obj = new Grid(parent,show); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Grid::Grid" ); return 0; } catch(std::exception& e) { pbSetError("Grid::Grid",e.what()); return -1; } } + //! create new & copy content from another grid + Grid(const Grid<T>& a); + //! return memory to solver + virtual ~Grid(); + + typedef T BASETYPE; + + void save(std::string name); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::save"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->save(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::save"); return _retval; } catch(std::exception& e) { pbSetError("Grid::save",e.what()); return 0; } } + void load(std::string name); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::load"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->load(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::load"); return _retval; } catch(std::exception& e) { pbSetError("Grid::load",e.what()); return 0; } } + //! loads values to be added to existing grid + void loadIncrement(std::string name); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::loadIncrement"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->loadIncrement(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::loadIncrement"); return _retval; } catch(std::exception& e) { pbSetError("Grid::loadIncrement",e.what()); return 0; } } + //! set all cells to zero + void clear(); static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::clear"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->clear(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::clear"); return _retval; } catch(std::exception& e) { pbSetError("Grid::clear",e.what()); return 0; } } + + //! all kinds of access functions, use grid(), grid[] or grid.get() + //! access data + inline T get(int i,int j, int k) const { return mData[index(i,j,k)]; } + //! access data + inline T& get(int i,int j, int k) { return mData[index(i,j,k)]; } + //! access data + inline T get(int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T get(const Vec3i& pos) const { return mData[index(pos)]; } + //! access data + inline T& operator()(int i, int j, int k) { return mData[index(i, j, k)]; } + //! access data + inline T operator()(int i, int j, int k) const { return mData[index(i, j, k)]; } + //! access data + inline T& operator()(int idx) { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T operator()(int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline T& operator()(const Vec3i& pos) { return mData[index(pos)]; } + //! access data + inline T operator()(const Vec3i& pos) const { return mData[index(pos)]; } + //! access data + inline T& operator[](int idx) { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + //! access data + inline const T operator[](int idx) const { DEBUG_ONLY(checkIndex(idx)); return mData[idx]; } + + // interpolated access + inline T getInterpolated(const Vec3& pos) const { return interpol<T>(mData, mSize, mStrideZ, pos); } + inline void setInterpolated(const Vec3& pos, const T& val, Grid<Real>& sumBuffer) const { setInterpol<T>(mData, mSize, mStrideZ, pos, val, &sumBuffer[0]); } + // higher order interpolation + inline T getInterpolated(const Vec3& pos, int order) const { + switch(order) { + case 1: return interpol<T>(mData, mSize, mStrideZ, pos); + // case 2: return interpolCubic<T>(mData, mSize, mStrideZ, pos); + default: + assertMsg(false, "Unknown interpolation order "<<order); + } + } + + // assignment / copy + + //! warning - do not use "=" for grids in python, this copies the reference! not the grid content... + //Grid<T>& operator=(const Grid<T>& a); + //! copy content from other grid (use this one instead of operator= !) + Grid<T>& copyFrom(const Grid<T>& a); static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::copyFrom"); PyObject *_retval = 0; { ArgLocker _lock; const Grid<T>& a = *_args.getPtr<Grid<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->copyFrom(a)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::copyFrom"); return _retval; } catch(std::exception& e) { pbSetError("Grid::copyFrom",e.what()); return 0; } } // { *this = a; } + + // helper functions to work with grids in scene files + + //! add/subtract other grid + void add(const Grid<T>& a); static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::add"); PyObject *_retval = 0; { ArgLocker _lock; const Grid<T>& a = *_args.getPtr<Grid<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->add(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::add"); return _retval; } catch(std::exception& e) { pbSetError("Grid::add",e.what()); return 0; } } + void sub(const Grid<T>& a); static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::sub"); PyObject *_retval = 0; { ArgLocker _lock; const Grid<T>& a = *_args.getPtr<Grid<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->sub(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::sub"); return _retval; } catch(std::exception& e) { pbSetError("Grid::sub",e.what()); return 0; } } + //! set all cells to constant value + void setConst(T s); static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::setConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::setConst"); return _retval; } catch(std::exception& e) { pbSetError("Grid::setConst",e.what()); return 0; } } + //! add constant to all grid cells + void addConst(T s); static PyObject* _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::addConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->addConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::addConst"); return _retval; } catch(std::exception& e) { pbSetError("Grid::addConst",e.what()); return 0; } } + //! add scaled other grid to current one (note, only "Real" factor, "T" type not supported here!) + void addScaled(const Grid<T>& a, const T& factor); static PyObject* _W_11 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::addScaled"); PyObject *_retval = 0; { ArgLocker _lock; const Grid<T>& a = *_args.getPtr<Grid<T> >("a",0,&_lock); const T& factor = *_args.getPtr<T >("factor",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->addScaled(a,factor); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::addScaled"); return _retval; } catch(std::exception& e) { pbSetError("Grid::addScaled",e.what()); return 0; } } + //! multiply contents of grid + void mult( const Grid<T>& a); static PyObject* _W_12 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::mult"); PyObject *_retval = 0; { ArgLocker _lock; const Grid<T>& a = *_args.getPtr<Grid<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->mult(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::mult"); return _retval; } catch(std::exception& e) { pbSetError("Grid::mult",e.what()); return 0; } } + //! multiply each cell by a constant scalar value + void multConst(T s); static PyObject* _W_13 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::multConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->multConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::multConst"); return _retval; } catch(std::exception& e) { pbSetError("Grid::multConst",e.what()); return 0; } } + //! clamp content to range (for vec3, clamps each component separately) + void clamp(Real min, Real max); static PyObject* _W_14 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::clamp"); PyObject *_retval = 0; { ArgLocker _lock; Real min = _args.get<Real >("min",0,&_lock); Real max = _args.get<Real >("max",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->clamp(min,max); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::clamp"); return _retval; } catch(std::exception& e) { pbSetError("Grid::clamp",e.what()); return 0; } } + + // common compound operators + //! get absolute max value in grid + Real getMaxAbsValue(); static PyObject* _W_15 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::getMaxAbsValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMaxAbsValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::getMaxAbsValue"); return _retval; } catch(std::exception& e) { pbSetError("Grid::getMaxAbsValue",e.what()); return 0; } } + //! get max value in grid + Real getMaxValue(); static PyObject* _W_16 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::getMaxValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMaxValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::getMaxValue"); return _retval; } catch(std::exception& e) { pbSetError("Grid::getMaxValue",e.what()); return 0; } } + //! get min value in grid + Real getMinValue(); static PyObject* _W_17 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::getMinValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMinValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::getMinValue"); return _retval; } catch(std::exception& e) { pbSetError("Grid::getMinValue",e.what()); return 0; } } + + //! debugging helper, print grid from python + void printGrid(int zSlice=-1, bool printIndex=false); static PyObject* _W_18 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::printGrid"); PyObject *_retval = 0; { ArgLocker _lock; int zSlice = _args.getOpt<int >("zSlice",0,-1,&_lock); bool printIndex = _args.getOpt<bool >("printIndex",1,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->printGrid(zSlice,printIndex); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::printGrid"); return _retval; } catch(std::exception& e) { pbSetError("Grid::printGrid",e.what()); return 0; } } + + //! write and read grid data to pointed memory + void writeGridToMemory(const std::string& memLoc, const std::string& sizeAllowed); static PyObject* _W_19 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::writeGridToMemory"); PyObject *_retval = 0; { ArgLocker _lock; const std::string& memLoc = _args.get<std::string >("memLoc",0,&_lock); const std::string& sizeAllowed = _args.get<std::string >("sizeAllowed",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->writeGridToMemory(memLoc,sizeAllowed); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::writeGridToMemory"); return _retval; } catch(std::exception& e) { pbSetError("Grid::writeGridToMemory",e.what()); return 0; } } + void readGridFromMemory(const std::string& memLoc, int x, int y, int z); static PyObject* _W_20 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::readGridFromMemory"); PyObject *_retval = 0; { ArgLocker _lock; const std::string& memLoc = _args.get<std::string >("memLoc",0,&_lock); int x = _args.get<int >("x",1,&_lock); int y = _args.get<int >("y",2,&_lock); int z = _args.get<int >("z",3,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->readGridFromMemory(memLoc,x,y,z); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::readGridFromMemory"); return _retval; } catch(std::exception& e) { pbSetError("Grid::readGridFromMemory",e.what()); return 0; } } + void readAdaptiveGridFromMemory(const std::string& memLoc, Vec3i min, Vec3i max); static PyObject* _W_21 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::readAdaptiveGridFromMemory"); PyObject *_retval = 0; { ArgLocker _lock; const std::string& memLoc = _args.get<std::string >("memLoc",0,&_lock); Vec3i min = _args.get<Vec3i >("min",1,&_lock); Vec3i max = _args.get<Vec3i >("max",2,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->readAdaptiveGridFromMemory(memLoc,min,max); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::readAdaptiveGridFromMemory"); return _retval; } catch(std::exception& e) { pbSetError("Grid::readAdaptiveGridFromMemory",e.what()); return 0; } } + //! Applies texture to grid, as in Shape::applyToGrid + void applyToGrid(GridBase *grid, FlagGrid* respectFlags = 0); static PyObject* _W_22 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::applyToGrid"); PyObject *_retval = 0; { ArgLocker _lock; GridBase* grid = _args.getPtr<GridBase >("grid",0,&_lock); FlagGrid* respectFlags = _args.getPtrOpt<FlagGrid >("respectFlags",1,0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->applyToGrid(grid,respectFlags); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::applyToGrid"); return _retval; } catch(std::exception& e) { pbSetError("Grid::applyToGrid",e.what()); return 0; } } + std::string getDataPointer(); static PyObject* _W_23 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Grid* pbo = dynamic_cast<Grid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Grid::getDataPointer"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getDataPointer()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Grid::getDataPointer"); return _retval; } catch(std::exception& e) { pbSetError("Grid::getDataPointer",e.what()); return 0; } } + // c++ only operators + template<class S> Grid<T>& operator+=(const Grid<S>& a); + template<class S> Grid<T>& operator+=(const S& a); + template<class S> Grid<T>& operator-=(const Grid<S>& a); + template<class S> Grid<T>& operator-=(const S& a); + template<class S> Grid<T>& operator*=(const Grid<S>& a); + template<class S> Grid<T>& operator*=(const S& a); + template<class S> Grid<T>& operator/=(const Grid<S>& a); + template<class S> Grid<T>& operator/=(const S& a); + Grid<T>& safeDivide(const Grid<T>& a); + + //! Swap data with another grid (no actual data is moved) + void swap(Grid<T>& other); + +protected: T* mData; public: PbArgs _args;} +#define _C_Grid +; + +// Python doesn't know about templates: explicit aliases needed + + + + +//! Special function for staggered grids +class MACGrid : public Grid<Vec3> {public: + MACGrid(FluidSolver* parent, bool show=true) :Grid<Vec3>(parent,show){ + mType = (GridType)(TypeMAC | TypeVec3); } static int _W_24 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "MACGrid::MACGrid" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); bool show = _args.getOpt<bool >("show",1,true,&_lock); obj = new MACGrid(parent,show); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"MACGrid::MACGrid" ); return 0; } catch(std::exception& e) { pbSetError("MACGrid::MACGrid",e.what()); return -1; } } + + // specialized functions for interpolating MAC information + inline Vec3 getCentered(int i, int j, int k) const; + inline Vec3 getCentered(const Vec3i& pos) const { return getCentered(pos.x, pos.y, pos.z); } + inline Vec3 getAtMACX(int i, int j, int k) const; + inline Vec3 getAtMACY(int i, int j, int k) const; + inline Vec3 getAtMACZ(int i, int j, int k) const; + template<int comp> inline Real getInterpolatedComponent(Vec3 pos) const { return interpolComponent<comp>(mData, mSize, mStrideZ, pos); } + inline Vec3 getInterpolated(const Vec3& pos) const { return interpolMAC(mData, mSize, mStrideZ, pos); } + inline void setInterpolated(const Vec3& pos, const Vec3& val, Vec3* tmp) { return setInterpolMAC(mData, mSize, mStrideZ, pos, val, tmp); } + protected: public: PbArgs _args;} +#define _C_MACGrid +; + +//! Special functions for FlagGrid +class FlagGrid : public Grid<int> {public: + FlagGrid(FluidSolver* parent, int dim=3, bool show=true) :Grid<int>(parent,show){ + mType = (GridType)(TypeFlags | TypeInt); } static int _W_25 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "FlagGrid::FlagGrid" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); int dim = _args.getOpt<int >("dim",1,3,&_lock); bool show = _args.getOpt<bool >("show",2,true,&_lock); obj = new FlagGrid(parent,dim,show); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"FlagGrid::FlagGrid" ); return 0; } catch(std::exception& e) { pbSetError("FlagGrid::FlagGrid",e.what()); return -1; } } + + //! types of cells, in/outflow can be combined, e.g., TypeFluid|TypeInflow + enum CellType { + TypeNone = 0, + TypeFluid = 1, + TypeObstacle = 2, + TypeEmpty = 4, + TypeInflow = 8, + TypeOutflow = 16, + TypeStick = 128, + TypeReserved = 256 + // 2^10 - 2^14 reserved for moving obstacles + }; + + // MLE 2014-06-25 + int bWidth; + inline int getBoundaryWidth(){return bWidth;}; + + //! access for particles + inline int getAt(const Vec3& pos) const { return mData[index((int)pos.x, (int)pos.y, (int)pos.z)]; } + + //! check for different flag types + inline bool isObstacle(int idx) const { return get(idx) & TypeObstacle; } + inline bool isObstacle(int i, int j, int k) const { return get(i,j,k) & TypeObstacle; } + inline bool isObstacle(const Vec3i& pos) const { return get(pos) & TypeObstacle; } + inline bool isObstacle(const Vec3& pos) const { return getAt(pos) & TypeObstacle; } + inline bool isFluid(int idx) const { return get(idx) & TypeFluid; } + inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & TypeFluid; } + inline bool isFluid(const Vec3i& pos) const { return get(pos) & TypeFluid; } + inline bool isFluid(const Vec3& pos) const { return getAt(pos) & TypeFluid; } + inline bool isInflow(int idx) const { return get(idx) & TypeInflow; } + inline bool isInflow(int i, int j, int k) const { return get(i,j,k) & TypeInflow; } + inline bool isInflow(const Vec3i& pos) const { return get(pos) & TypeInflow; } + inline bool isInflow(const Vec3& pos) const { return getAt(pos) & TypeInflow; } + inline bool isEmpty(int idx) const { return get(idx) & TypeEmpty; } + inline bool isEmpty(int i, int j, int k) const { return get(i,j,k) & TypeEmpty; } + inline bool isEmpty(const Vec3i& pos) const { return get(pos) & TypeEmpty; } + inline bool isEmpty(const Vec3& pos) const { return getAt(pos) & TypeEmpty; } + inline bool isStick(int idx) const { return get(idx) & TypeStick; } + inline bool isStick(int i, int j, int k) const { return get(i,j,k) & TypeStick; } + inline bool isStick(const Vec3i& pos) const { return get(pos) & TypeStick; } + inline bool isStick(const Vec3& pos) const { return getAt(pos) & TypeStick; } + + // Python callables + void initDomain(int boundaryWidth=0); static PyObject* _W_26 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FlagGrid* pbo = dynamic_cast<FlagGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FlagGrid::initDomain"); PyObject *_retval = 0; { ArgLocker _lock; int boundaryWidth = _args.getOpt<int >("boundaryWidth",0,0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->initDomain(boundaryWidth); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FlagGrid::initDomain"); return _retval; } catch(std::exception& e) { pbSetError("FlagGrid::initDomain",e.what()); return 0; } } + void initBoundaries(int boundaryWidth=0); static PyObject* _W_27 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FlagGrid* pbo = dynamic_cast<FlagGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FlagGrid::initBoundaries"); PyObject *_retval = 0; { ArgLocker _lock; int boundaryWidth = _args.getOpt<int >("boundaryWidth",0,0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->initBoundaries(boundaryWidth); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FlagGrid::initBoundaries"); return _retval; } catch(std::exception& e) { pbSetError("FlagGrid::initBoundaries",e.what()); return 0; } } + void updateFromLevelset(LevelsetGrid& levelset); static PyObject* _W_28 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FlagGrid* pbo = dynamic_cast<FlagGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FlagGrid::updateFromLevelset"); PyObject *_retval = 0; { ArgLocker _lock; LevelsetGrid& levelset = *_args.getPtr<LevelsetGrid >("levelset",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->updateFromLevelset(levelset); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FlagGrid::updateFromLevelset"); return _retval; } catch(std::exception& e) { pbSetError("FlagGrid::updateFromLevelset",e.what()); return 0; } } void fillGrid(int type=TypeFluid); static PyObject* _W_29 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FlagGrid* pbo = dynamic_cast<FlagGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "FlagGrid::fillGrid"); PyObject *_retval = 0; { ArgLocker _lock; int type = _args.getOpt<int >("type",0,TypeFluid,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->fillGrid(type); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"FlagGrid::fillGrid"); return _retval; } catch(std::exception& e) { pbSetError("FlagGrid::fillGrid",e.what()); return 0; } } public: PbArgs _args;} +#define _C_FlagGrid +; + +//! helper to compute grid conversion factor between local coordinates of two grids +inline Vec3 calcGridSizeFactor(Vec3i s1, Vec3i s2) { + return Vec3( Real(s1[0])/s2[0], Real(s1[1])/s2[1], Real(s1[2])/s2[2] ); +} + + +//****************************************************************************** +// enable compilation of a more complicated test data type +// for grids... note - this also enables code parts in fileio.cpp! +// the code below is meant only as an example for a grid with a more complex data type +// and illustrates which functions need to be implemented; it's not needed +// to run any simulations in mantaflow! + +#define ENABLE_GRID_TEST_DATATYPE 0 + +#if ENABLE_GRID_TEST_DATATYPE==1 + +typedef std::vector<int> nbVectorBaseType; +class nbVector : public nbVectorBaseType { + public: + inline nbVector() : nbVectorBaseType() {}; + inline ~nbVector() {}; + + // grid operators require certain functions + inline nbVector(Real v) : nbVectorBaseType() { this->push_back( (int)v ); }; + + inline const nbVector& operator+= ( const nbVector &v1 ) { + assertMsg(false,"Never call!"); return *this; + } + inline const nbVector& operator-= ( const nbVector &v1 ) { + assertMsg(false,"Never call!"); return *this; + } + inline const nbVector& operator*= ( const nbVector &v1 ) { + assertMsg(false,"Never call!"); return *this; + } +}; + +template<> inline nbVector* FluidSolver::getGridPointer<nbVector>() { + return new nbVector[mGridSize.x * mGridSize.y * mGridSize.z]; +} +template<> inline void FluidSolver::freeGridPointer<nbVector>(nbVector* ptr) { + return delete[] ptr; +} + +inline nbVector operator+ ( const nbVector &v1, const nbVector &v2 ) { + assertMsg(false,"Never call!"); return nbVector(); +} +inline nbVector operator* ( const nbVector &v1, const nbVector &v2 ) { + assertMsg(false,"Never call!"); return nbVector(); +} +template<class S> +inline nbVector operator* ( const nbVector& v, S s ) { + assertMsg(false,"Never call!"); return nbVector(); +} +template<class S> +inline nbVector operator* ( S s, const nbVector& v ) { + assertMsg(false,"Never call!"); return nbVector(); +} + +template<> inline nbVector safeDivide<nbVector>(const nbVector &a, const nbVector& b) { + assertMsg(false,"Never call!"); return nbVector(); +} + +std::ostream& operator<< ( std::ostream& os, const nbVectorBaseType& i ) { + os << " nbVectorBaseType NYI "; + return os; +} + +// make data type known to python +// (python keyword changed here, because the preprocessor does not yet parse #ifdefs correctly) +PY THON alias Grid<nbVector> TestDataGrid; +// ? PY THON alias nbVector TestDatatype; + +#endif // end ENABLE_GRID_TEST_DATATYPE + + + +//****************************************************************************** +// Implementation of inline functions + +inline void GridBase::checkIndex(int i, int j, int k) const { + //if (i<0 || j<0 || i>=mSize.x || j>=mSize.y || (is3D() && (k<0|| k>= mSize.z))) { + if (i<0 || j<0 || i>=mSize.x || j>=mSize.y || k<0|| k>= mSize.z ) { + std::ostringstream s; + s << "Grid " << mName << " dim " << mSize << " : index " << i << "," << j << "," << k << " out of bound "; + errMsg(s.str()); + } +} + +inline void GridBase::checkIndex(int idx) const { + if (idx<0 || idx >= mSize.x * mSize.y * mSize.z) { + std::ostringstream s; + s << "Grid " << mName << " dim " << mSize << " : index " << idx << " out of bound "; + errMsg(s.str()); + } +} + +bool GridBase::isInBounds(const Vec3i& p) const { + return (p.x >= 0 && p.y >= 0 && p.z >= 0 && p.x < mSize.x && p.y < mSize.y && p.z < mSize.z); +} + +bool GridBase::isInBounds(const Vec3i& p, int bnd) const { + bool ret = (p.x >= bnd && p.y >= bnd && p.x < mSize.x-bnd && p.y < mSize.y-bnd); + if(this->is3D()) { + ret &= (p.z >= bnd && p.z < mSize.z-bnd); + } else { + ret &= (p.z == 0); + } + return ret; +} +//! Check if linear index is in the range of the array +bool GridBase::isInBounds(int idx) const { + if (idx<0 || idx >= mSize.x * mSize.y * mSize.z) { + return false; + } + return true; +} + +inline Vec3 MACGrid::getCentered(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i+1,j+1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3(0.5* (mData[idx].x + mData[idx+1].x), + 0.5* (mData[idx].y + mData[idx+mSize.x].y), + 0.); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ)); + v[2] = 0.5* (mData[idx].z + mData[idx+mStrideZ].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACX(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i-1,j+1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3( (mData[idx].x), + 0.25* (mData[idx].y + mData[idx-1].y + mData[idx+mSize.x].y + mData[idx+mSize.x-1].y), + 0.); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ-1)); + v[2] = 0.25* (mData[idx].z + mData[idx-1].z + mData[idx+mStrideZ].z + mData[idx+mStrideZ-1].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACY(int i, int j, int k) const { + DEBUG_ONLY(checkIndex(i+1,j-1,k)); + const int idx = index(i,j,k); + Vec3 v = Vec3(0.25* (mData[idx].x + mData[idx-mSize.x].x + mData[idx+1].x + mData[idx+1-mSize.x].x), + (mData[idx].y), 0. ); + if( this->is3D() ) { + DEBUG_ONLY(checkIndex(idx+mStrideZ-mSize.x)); + v[2] = 0.25* (mData[idx].z + mData[idx-mSize.x].z + mData[idx+mStrideZ].z + mData[idx+mStrideZ-mSize.x].z); + } + return v; +} + +inline Vec3 MACGrid::getAtMACZ(int i, int j, int k) const { + const int idx = index(i,j,k); + DEBUG_ONLY(checkIndex(idx-mStrideZ)); + DEBUG_ONLY(checkIndex(idx+mSize.x-mStrideZ)); + Vec3 v = Vec3(0.25* (mData[idx].x + mData[idx-mStrideZ].x + mData[idx+1].x + mData[idx+1-mStrideZ].x), + 0.25* (mData[idx].y + mData[idx-mStrideZ].y + mData[idx+mSize.x].y + mData[idx+mSize.x-mStrideZ].y), + (mData[idx].z) ); + return v; +} + +template <class T, class S> struct gridAdd : public KernelBase { gridAdd(Grid<T>& me, const Grid<S>& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const Grid<S>& other ) { me[idx] += other[idx]; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<S>& getArg1() { return other; } typedef Grid<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const Grid<S>& other; }; +template <class T, class S> struct gridSub : public KernelBase { gridSub(Grid<T>& me, const Grid<S>& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const Grid<S>& other ) { me[idx] -= other[idx]; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<S>& getArg1() { return other; } typedef Grid<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const Grid<S>& other; }; +template <class T, class S> struct gridMult : public KernelBase { gridMult(Grid<T>& me, const Grid<S>& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const Grid<S>& other ) { me[idx] *= other[idx]; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<S>& getArg1() { return other; } typedef Grid<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const Grid<S>& other; }; +template <class T, class S> struct gridDiv : public KernelBase { gridDiv(Grid<T>& me, const Grid<S>& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const Grid<S>& other ) { me[idx] /= other[idx]; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<S>& getArg1() { return other; } typedef Grid<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const Grid<S>& other; }; +template <class T, class S> struct gridAddScalar : public KernelBase { gridAddScalar(Grid<T>& me, const S& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const S& other ) { me[idx] += other; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const S& getArg1() { return other; } typedef S type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const S& other; }; +template <class T, class S> struct gridMultScalar : public KernelBase { gridMultScalar(Grid<T>& me, const S& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const S& other ) { me[idx] *= other; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const S& getArg1() { return other; } typedef S type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const S& other; }; +template <class T, class S> struct gridScaledAdd : public KernelBase { gridScaledAdd(Grid<T>& me, const Grid<T>& other, const S& factor) : KernelBase(&me,0) ,me(me),other(other),factor(factor) { run(); } inline void op(int idx, Grid<T>& me, const Grid<T>& other, const S& factor ) { me[idx] += factor * other[idx]; } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<T>& getArg1() { return other; } typedef Grid<T> type1;inline const S& getArg2() { return factor; } typedef S type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other,factor); } Grid<T>& me; const Grid<T>& other; const S& factor; }; + +template <class T> struct gridSafeDiv : public KernelBase { gridSafeDiv(Grid<T>& me, const Grid<T>& other) : KernelBase(&me,0) ,me(me),other(other) { run(); } inline void op(int idx, Grid<T>& me, const Grid<T>& other ) { me[idx] = safeDivide(me[idx], other[idx]); } inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<T>& getArg1() { return other; } typedef Grid<T> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } Grid<T>& me; const Grid<T>& other; }; +template <class T> struct gridSetConst : public KernelBase { gridSetConst(Grid<T>& grid, T value) : KernelBase(&grid,0) ,grid(grid),value(value) { run(); } inline void op(int idx, Grid<T>& grid, T value ) { grid[idx] = value; } inline Grid<T>& getArg0() { return grid; } typedef Grid<T> type0;inline T& getArg1() { return value; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, grid,value); } Grid<T>& grid; T value; }; + +template<class T> template<class S> Grid<T>& Grid<T>::operator+= (const Grid<S>& a) { + gridAdd<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator+= (const S& a) { + gridAddScalar<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator-= (const Grid<S>& a) { + gridSub<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator-= (const S& a) { + gridAddScalar<T,S> (*this, -a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator*= (const Grid<S>& a) { + gridMult<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator*= (const S& a) { + gridMultScalar<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator/= (const Grid<S>& a) { + gridDiv<T,S> (*this, a); + return *this; +} +template<class T> template<class S> Grid<T>& Grid<T>::operator/= (const S& a) { + S rez((S)1.0 / a); + gridMultScalar<T,S> (*this, rez); + return *this; +} + + + +} //namespace +#endif + + diff --git a/source/blender/python/manta_pp/grid.h.reg b/source/blender/python/manta_pp/grid.h.reg new file mode 100644 index 00000000000..bd58e1d1590 --- /dev/null +++ b/source/blender/python/manta_pp/grid.h.reg @@ -0,0 +1,46 @@ +#include "grid.h" ++GridBase^ static const Pb::Register _R_$IDX$ ("GridBase","GridBase","PbClass"); template<> const char* Namify<GridBase >::S = "GridBase"; +>GridBase^ ++GridBase^ static const Pb::Register _R_$IDX$ ("GridBase","GridBase",GridBase::_W_0); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","Grid<$CT$>","GridBase"); template<> const char* Namify<Grid<$CT$> >::S = "Grid<$CT$>"; ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","Grid",Grid<$CT$>::_W_1); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","save",Grid<$CT$>::_W_2); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","load",Grid<$CT$>::_W_3); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","loadIncrement",Grid<$CT$>::_W_4); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","clear",Grid<$CT$>::_W_5); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","copyFrom",Grid<$CT$>::_W_6); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","add",Grid<$CT$>::_W_7); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","sub",Grid<$CT$>::_W_8); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","setConst",Grid<$CT$>::_W_9); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","addConst",Grid<$CT$>::_W_10); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","addScaled",Grid<$CT$>::_W_11); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","mult",Grid<$CT$>::_W_12); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","multConst",Grid<$CT$>::_W_13); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","clamp",Grid<$CT$>::_W_14); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","getMaxAbsValue",Grid<$CT$>::_W_15); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","getMaxValue",Grid<$CT$>::_W_16); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","getMinValue",Grid<$CT$>::_W_17); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","printGrid",Grid<$CT$>::_W_18); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","writeGridToMemory",Grid<$CT$>::_W_19); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","readGridFromMemory",Grid<$CT$>::_W_20); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","readAdaptiveGridFromMemory",Grid<$CT$>::_W_21); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","applyToGrid",Grid<$CT$>::_W_22); ++Grid^ static const Pb::Register _R_$IDX$ ("Grid<$CT$>","getDataPointer",Grid<$CT$>::_W_23); +>Grid^int +&static const Pb::Register _R_$IDX$ ("Grid<int>","IntGrid",""); +>Grid^Real +&static const Pb::Register _R_$IDX$ ("Grid<Real>","RealGrid",""); +>Grid^Vec3 +&static const Pb::Register _R_$IDX$ ("Grid<Vec3>","VecGrid",""); ++MACGrid^ static const Pb::Register _R_$IDX$ ("MACGrid","MACGrid","Grid<$BT$>"); template<> const char* Namify<MACGrid >::S = "MACGrid"; +>MACGrid^ +@MACGrid^^Grid^Vec3 ++MACGrid^ static const Pb::Register _R_$IDX$ ("MACGrid","MACGrid",MACGrid::_W_24); ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","FlagGrid","Grid<$BT$>"); template<> const char* Namify<FlagGrid >::S = "FlagGrid"; +>FlagGrid^ +@FlagGrid^^Grid^int ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","FlagGrid",FlagGrid::_W_25); ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","initDomain",FlagGrid::_W_26); ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","initBoundaries",FlagGrid::_W_27); ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","updateFromLevelset",FlagGrid::_W_28); ++FlagGrid^ static const Pb::Register _R_$IDX$ ("FlagGrid","fillGrid",FlagGrid::_W_29); diff --git a/source/blender/python/manta_pp/grid.h.reg.cpp b/source/blender/python/manta_pp/grid.h.reg.cpp new file mode 100644 index 00000000000..76321052a1d --- /dev/null +++ b/source/blender/python/manta_pp/grid.h.reg.cpp @@ -0,0 +1,107 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "grid.h" +namespace Manta { +#ifdef _C_FlagGrid + static const Pb::Register _R_3 ("FlagGrid","FlagGrid","Grid<int>"); template<> const char* Namify<FlagGrid >::S = "FlagGrid"; + static const Pb::Register _R_4 ("FlagGrid","FlagGrid",FlagGrid::_W_25); + static const Pb::Register _R_5 ("FlagGrid","initDomain",FlagGrid::_W_26); + static const Pb::Register _R_6 ("FlagGrid","initBoundaries",FlagGrid::_W_27); + static const Pb::Register _R_7 ("FlagGrid","updateFromLevelset",FlagGrid::_W_28); + static const Pb::Register _R_8 ("FlagGrid","fillGrid",FlagGrid::_W_29); +#endif +#ifdef _C_Grid + static const Pb::Register _R_9 ("Grid<int>","Grid<int>","GridBase"); template<> const char* Namify<Grid<int> >::S = "Grid<int>"; + static const Pb::Register _R_10 ("Grid<int>","Grid",Grid<int>::_W_1); + static const Pb::Register _R_11 ("Grid<int>","save",Grid<int>::_W_2); + static const Pb::Register _R_12 ("Grid<int>","load",Grid<int>::_W_3); + static const Pb::Register _R_13 ("Grid<int>","loadIncrement",Grid<int>::_W_4); + static const Pb::Register _R_14 ("Grid<int>","clear",Grid<int>::_W_5); + static const Pb::Register _R_15 ("Grid<int>","copyFrom",Grid<int>::_W_6); + static const Pb::Register _R_16 ("Grid<int>","add",Grid<int>::_W_7); + static const Pb::Register _R_17 ("Grid<int>","sub",Grid<int>::_W_8); + static const Pb::Register _R_18 ("Grid<int>","setConst",Grid<int>::_W_9); + static const Pb::Register _R_19 ("Grid<int>","addConst",Grid<int>::_W_10); + static const Pb::Register _R_20 ("Grid<int>","addScaled",Grid<int>::_W_11); + static const Pb::Register _R_21 ("Grid<int>","mult",Grid<int>::_W_12); + static const Pb::Register _R_22 ("Grid<int>","multConst",Grid<int>::_W_13); + static const Pb::Register _R_23 ("Grid<int>","clamp",Grid<int>::_W_14); + static const Pb::Register _R_24 ("Grid<int>","getMaxAbsValue",Grid<int>::_W_15); + static const Pb::Register _R_25 ("Grid<int>","getMaxValue",Grid<int>::_W_16); + static const Pb::Register _R_26 ("Grid<int>","getMinValue",Grid<int>::_W_17); + static const Pb::Register _R_27 ("Grid<int>","printGrid",Grid<int>::_W_18); + static const Pb::Register _R_28 ("Grid<int>","writeGridToMemory",Grid<int>::_W_19); + static const Pb::Register _R_29 ("Grid<int>","readGridFromMemory",Grid<int>::_W_20); + static const Pb::Register _R_30 ("Grid<int>","readAdaptiveGridFromMemory",Grid<int>::_W_21); + static const Pb::Register _R_31 ("Grid<int>","applyToGrid",Grid<int>::_W_22); + static const Pb::Register _R_32 ("Grid<int>","getDataPointer",Grid<int>::_W_23); + static const Pb::Register _R_33 ("Grid<Real>","Grid<Real>","GridBase"); template<> const char* Namify<Grid<Real> >::S = "Grid<Real>"; + static const Pb::Register _R_34 ("Grid<Real>","Grid",Grid<Real>::_W_1); + static const Pb::Register _R_35 ("Grid<Real>","save",Grid<Real>::_W_2); + static const Pb::Register _R_36 ("Grid<Real>","load",Grid<Real>::_W_3); + static const Pb::Register _R_37 ("Grid<Real>","loadIncrement",Grid<Real>::_W_4); + static const Pb::Register _R_38 ("Grid<Real>","clear",Grid<Real>::_W_5); + static const Pb::Register _R_39 ("Grid<Real>","copyFrom",Grid<Real>::_W_6); + static const Pb::Register _R_40 ("Grid<Real>","add",Grid<Real>::_W_7); + static const Pb::Register _R_41 ("Grid<Real>","sub",Grid<Real>::_W_8); + static const Pb::Register _R_42 ("Grid<Real>","setConst",Grid<Real>::_W_9); + static const Pb::Register _R_43 ("Grid<Real>","addConst",Grid<Real>::_W_10); + static const Pb::Register _R_44 ("Grid<Real>","addScaled",Grid<Real>::_W_11); + static const Pb::Register _R_45 ("Grid<Real>","mult",Grid<Real>::_W_12); + static const Pb::Register _R_46 ("Grid<Real>","multConst",Grid<Real>::_W_13); + static const Pb::Register _R_47 ("Grid<Real>","clamp",Grid<Real>::_W_14); + static const Pb::Register _R_48 ("Grid<Real>","getMaxAbsValue",Grid<Real>::_W_15); + static const Pb::Register _R_49 ("Grid<Real>","getMaxValue",Grid<Real>::_W_16); + static const Pb::Register _R_50 ("Grid<Real>","getMinValue",Grid<Real>::_W_17); + static const Pb::Register _R_51 ("Grid<Real>","printGrid",Grid<Real>::_W_18); + static const Pb::Register _R_52 ("Grid<Real>","writeGridToMemory",Grid<Real>::_W_19); + static const Pb::Register _R_53 ("Grid<Real>","readGridFromMemory",Grid<Real>::_W_20); + static const Pb::Register _R_54 ("Grid<Real>","readAdaptiveGridFromMemory",Grid<Real>::_W_21); + static const Pb::Register _R_55 ("Grid<Real>","applyToGrid",Grid<Real>::_W_22); + static const Pb::Register _R_56 ("Grid<Real>","getDataPointer",Grid<Real>::_W_23); + static const Pb::Register _R_57 ("Grid<Vec3>","Grid<Vec3>","GridBase"); template<> const char* Namify<Grid<Vec3> >::S = "Grid<Vec3>"; + static const Pb::Register _R_58 ("Grid<Vec3>","Grid",Grid<Vec3>::_W_1); + static const Pb::Register _R_59 ("Grid<Vec3>","save",Grid<Vec3>::_W_2); + static const Pb::Register _R_60 ("Grid<Vec3>","load",Grid<Vec3>::_W_3); + static const Pb::Register _R_61 ("Grid<Vec3>","loadIncrement",Grid<Vec3>::_W_4); + static const Pb::Register _R_62 ("Grid<Vec3>","clear",Grid<Vec3>::_W_5); + static const Pb::Register _R_63 ("Grid<Vec3>","copyFrom",Grid<Vec3>::_W_6); + static const Pb::Register _R_64 ("Grid<Vec3>","add",Grid<Vec3>::_W_7); + static const Pb::Register _R_65 ("Grid<Vec3>","sub",Grid<Vec3>::_W_8); + static const Pb::Register _R_66 ("Grid<Vec3>","setConst",Grid<Vec3>::_W_9); + static const Pb::Register _R_67 ("Grid<Vec3>","addConst",Grid<Vec3>::_W_10); + static const Pb::Register _R_68 ("Grid<Vec3>","addScaled",Grid<Vec3>::_W_11); + static const Pb::Register _R_69 ("Grid<Vec3>","mult",Grid<Vec3>::_W_12); + static const Pb::Register _R_70 ("Grid<Vec3>","multConst",Grid<Vec3>::_W_13); + static const Pb::Register _R_71 ("Grid<Vec3>","clamp",Grid<Vec3>::_W_14); + static const Pb::Register _R_72 ("Grid<Vec3>","getMaxAbsValue",Grid<Vec3>::_W_15); + static const Pb::Register _R_73 ("Grid<Vec3>","getMaxValue",Grid<Vec3>::_W_16); + static const Pb::Register _R_74 ("Grid<Vec3>","getMinValue",Grid<Vec3>::_W_17); + static const Pb::Register _R_75 ("Grid<Vec3>","printGrid",Grid<Vec3>::_W_18); + static const Pb::Register _R_76 ("Grid<Vec3>","writeGridToMemory",Grid<Vec3>::_W_19); + static const Pb::Register _R_77 ("Grid<Vec3>","readGridFromMemory",Grid<Vec3>::_W_20); + static const Pb::Register _R_78 ("Grid<Vec3>","readAdaptiveGridFromMemory",Grid<Vec3>::_W_21); + static const Pb::Register _R_79 ("Grid<Vec3>","applyToGrid",Grid<Vec3>::_W_22); + static const Pb::Register _R_80 ("Grid<Vec3>","getDataPointer",Grid<Vec3>::_W_23); +#endif +#ifdef _C_GridBase + static const Pb::Register _R_81 ("GridBase","GridBase","PbClass"); template<> const char* Namify<GridBase >::S = "GridBase"; + static const Pb::Register _R_82 ("GridBase","GridBase",GridBase::_W_0); +#endif +#ifdef _C_MACGrid + static const Pb::Register _R_83 ("MACGrid","MACGrid","Grid<Vec3>"); template<> const char* Namify<MACGrid >::S = "MACGrid"; + static const Pb::Register _R_84 ("MACGrid","MACGrid",MACGrid::_W_24); +#endif +static const Pb::Register _R_0 ("Grid<int>","IntGrid",""); +static const Pb::Register _R_1 ("Grid<Real>","RealGrid",""); +static const Pb::Register _R_2 ("Grid<Vec3>","VecGrid",""); +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/gui/customctrl.cpp b/source/blender/python/manta_pp/gui/customctrl.cpp new file mode 100644 index 00000000000..c7d4defeca7 --- /dev/null +++ b/source/blender/python/manta_pp/gui/customctrl.cpp @@ -0,0 +1,198 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GUI extension from python + * + ******************************************************************************/ + +#include "customctrl.h" +#include "qtmain.h" +#include "painter.h" + +using namespace std; +namespace Manta { + +// ***************************************************************************** +// Slider class + +CustomControl::CustomControl() : PbClass(0) { +} + +CustomCheckbox::CustomCheckbox(string name, bool val) : mVal(val), mSName(name), mCheckbox(0) { +} + +void CustomCheckbox::init(QBoxLayout* layout) { + mCheckbox = new TextCheckbox(mSName, mVal); + QObject::connect(mCheckbox, SIGNAL(stateChanged(int)), mCheckbox, SLOT(update(int))); + mCheckbox->attach(layout); +} + +bool CustomCheckbox::get() { + if (!mCheckbox) throw Error("Slider is not attached yet!"); + return mCheckbox->get(); +} +void CustomCheckbox::set(bool v) { + if (!mCheckbox) throw Error("Slider is not attached yet!"); + mCheckbox->set(v); +} + +CustomSlider::CustomSlider(string name, float val, float min, float max) : + mMin(min), mMax(max), mVal(val), mSName(name), mSlider(0) +{ +} + +void CustomSlider::init(QBoxLayout* layout) { + mSlider = new TextSlider(mSName, mVal, mMin, mMax); + QObject::connect(mSlider, SIGNAL(valueChanged(int)), mSlider, SLOT(update(int))); + mSlider->attach(layout); +} + +float CustomSlider::get() { + if (!mSlider) throw Error("Slider is not attached yet!"); + return mSlider->get(); +} + +void CustomSlider::set(float v) { + if (!mSlider) throw Error("Slider is not attached yet!"); + mSlider->set(v); +} + +TextSlider::TextSlider(const string& name, float val, float vmin, float vmax) : + QSlider(Qt::Horizontal), mMin(vmin), mMax(vmax), mSName(name.c_str()) +{ + mLabel = new QLabel(); + mScale = 1000; + setMinimum(0); + setMaximum(999); + set(val); + update(0); + } + +void TextSlider::attach(QBoxLayout* layout) { + layout->addWidget(mLabel); + layout->addWidget(this); +} + +void TextSlider::update(int val) { + float v = get(); + QString num; + num.sprintf("%.2g", v); + mLabel->setText(mSName + ": " + num); +} + +float TextSlider::get() { + float va = mMin + (mMax-mMin) / mScale * (float)value(); + return clamp(va, mMin, mMax); +} + +void TextSlider::set(float v) { + float va = clamp(v, mMin, mMax); + va = (va - mMin) / (mMax-mMin) * mScale; + setValue((int)(va+0.5)); +} + +TextCheckbox::TextCheckbox(const string& name, bool val) : + QCheckBox(), mVal(val), mSName(name.c_str()) +{ + mLabel = new QLabel(); + set(val); + mLabel->setText(mSName); + } + +void TextCheckbox::attach(QBoxLayout* layout) { + QLayout* lay = new QHBoxLayout; + lay->setAlignment(Qt::AlignLeft); + lay->addWidget(this); + lay->addWidget(mLabel); + layout->addLayout(lay); +} + +void TextCheckbox::update(int val) { +} + +bool TextCheckbox::get() { + return isChecked(); +} + +void TextCheckbox::set(bool v) { + setChecked(v); +} + + + +// ************************************************************************************** +// GUI class + +void updateQtGui(bool full, int frame, const std::string& curPlugin); +extern MainThread* gMainThread; +extern GuiThread* gGuiThread; + +Gui::Gui() : + PbClass(NULL), mGuiPtr(gGuiThread), mMainPtr(gMainThread) { +} + +void Gui::setBackgroundMesh(Mesh* m) { + mGuiPtr->getWindow()->setBackground(m); +} +void Gui::show(bool twoD) { + if(twoD) + mMainPtr->send( (int)MainWnd::EventSet2DCam ); + mMainPtr->sendAndWait( (int)MainWnd::EventGuiShow ); +} +void Gui::update() { + updateQtGui(true,-1,""); +} +void Gui::pause() { + mMainPtr->sendAndWait((int)MainWnd::EventFullUpdate); + mGuiPtr->getWindow()->pause(); +} +void Gui::screenshot(string filename) { + QString s(filename.c_str()); + QMetaObject::invokeMethod(mGuiPtr->getWindow(), "screenshot", Q_ARG(QString, s)); +} + +void Gui::nextRealGrid() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextRealGrid" ); } +void Gui::nextVec3Grid() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextVec3Grid" ); } +void Gui::nextParts() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextParts" ); } +void Gui::nextPdata() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextPdata" ); } +void Gui::nextMesh() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextMesh" ); } + +void Gui::nextVec3Display() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextVec3Display" ); } +void Gui::nextPartDisplay() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextPartDisplay" ); } +void Gui::nextMeshDisplay() { QMetaObject::invokeMethod(mGuiPtr->getWindow(), "nextMeshDisplay" ); } + +PbClass* Gui::addControl(PbType t) { + _args.add("nocheck",true); + if (t.str() == "") + throw Error("Need to specify object type. Use e.g. gui.create(Slider, ...)"); + + PbClass* obj = PbClass::createPyObject(t.str(), "", _args, this); + if (!obj || !obj->canConvertTo("CustomControl")) + throw Error("gui.create() can only create CustomControl-based objects"); + + QMetaObject::invokeMethod(gGuiThread->getWindow(), "addControl", Q_ARG(void*, (void*)obj)); + + return obj; +} + + +} // namespace + + diff --git a/source/blender/python/manta_pp/gui/customctrl.h b/source/blender/python/manta_pp/gui/customctrl.h new file mode 100644 index 00000000000..8dcd2a6c885 --- /dev/null +++ b/source/blender/python/manta_pp/gui/customctrl.h @@ -0,0 +1,148 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * GUI extension from python + * + ******************************************************************************/ + +#ifndef _CUSTOMCTRL_H__ +#define _CUSTOMCTRL_H__ + +#include <QSlider> +#include <QLabel> +#include <QCheckBox> +#include <QBoxLayout> +#include "manta.h" + +namespace Manta { + +// fwd decl. +class Mesh; +class GuiThread; +class MainThread; + +//! Interface for python declared controls +class CustomControl : public PbClass {public: + CustomControl(); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "CustomControl::CustomControl" ); { ArgLocker _lock; obj = new CustomControl(); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"CustomControl::CustomControl" ); return 0; } catch(std::exception& e) { pbSetError("CustomControl::CustomControl",e.what()); return -1; } } + + virtual void init(QBoxLayout* layout) {}; + protected: public: PbArgs _args;} +#define _C_CustomControl +; + +//! Checkbox with attached text display +class TextCheckbox : public QCheckBox { + Q_OBJECT +public: + TextCheckbox(const std::string& name, bool val); + void attach(QBoxLayout* layout); + void set(bool v); + bool get(); + +public slots: + void update(int v); + +protected: + bool mVal; + QLabel* mLabel; + QString mSName; +}; + +//! Slider with attached text display +class TextSlider : public QSlider { + Q_OBJECT +public: + TextSlider(const std::string& name, float val, float min, float max); + void attach(QBoxLayout* layout); + void set(float v); + float get(); + +public slots: + void update(int v); + +protected: + float mMin, mMax, mScale; + QLabel* mLabel; + QString mSName; +}; + +//! Links a slider control + +class CustomSlider : public CustomControl {public: + CustomSlider(std::string text, float val, float min, float max); static int _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "CustomSlider::CustomSlider" ); { ArgLocker _lock; std::string text = _args.get<std::string >("text",0,&_lock); float val = _args.get<float >("val",1,&_lock); float min = _args.get<float >("min",2,&_lock); float max = _args.get<float >("max",3,&_lock); obj = new CustomSlider(text,val,min,max); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"CustomSlider::CustomSlider" ); return 0; } catch(std::exception& e) { pbSetError("CustomSlider::CustomSlider",e.what()); return -1; } } + virtual void init(QBoxLayout* layout); + + float get(); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); CustomSlider* pbo = dynamic_cast<CustomSlider*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "CustomSlider::get"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->get()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"CustomSlider::get"); return _retval; } catch(std::exception& e) { pbSetError("CustomSlider::get",e.what()); return 0; } } + void set(float v); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); CustomSlider* pbo = dynamic_cast<CustomSlider*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "CustomSlider::set"); PyObject *_retval = 0; { ArgLocker _lock; float v = _args.get<float >("v",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->set(v); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"CustomSlider::set"); return _retval; } catch(std::exception& e) { pbSetError("CustomSlider::set",e.what()); return 0; } } + +protected: + float mMin, mMax, mVal; + std::string mSName; TextSlider* mSlider; public: PbArgs _args;} +#define _C_CustomSlider +; + +//! Links a checkbox control + +class CustomCheckbox : public CustomControl {public: + CustomCheckbox(std::string text, bool val); static int _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "CustomCheckbox::CustomCheckbox" ); { ArgLocker _lock; std::string text = _args.get<std::string >("text",0,&_lock); bool val = _args.get<bool >("val",1,&_lock); obj = new CustomCheckbox(text,val); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"CustomCheckbox::CustomCheckbox" ); return 0; } catch(std::exception& e) { pbSetError("CustomCheckbox::CustomCheckbox",e.what()); return -1; } } + virtual void init(QBoxLayout* layout); + + bool get(); static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); CustomCheckbox* pbo = dynamic_cast<CustomCheckbox*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "CustomCheckbox::get"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->get()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"CustomCheckbox::get"); return _retval; } catch(std::exception& e) { pbSetError("CustomCheckbox::get",e.what()); return 0; } } + void set(bool v); static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); CustomCheckbox* pbo = dynamic_cast<CustomCheckbox*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "CustomCheckbox::set"); PyObject *_retval = 0; { ArgLocker _lock; bool v = _args.get<bool >("v",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->set(v); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"CustomCheckbox::set"); return _retval; } catch(std::exception& e) { pbSetError("CustomCheckbox::set",e.what()); return 0; } } + +protected: + bool mVal; + std::string mSName; TextCheckbox* mCheckbox; public: PbArgs _args;} +#define _C_CustomCheckbox +; + + +//! GUI adapter class to call from Python +class Gui : public PbClass {public: + Gui(); static int _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Gui::Gui" ); { ArgLocker _lock; obj = new Gui(); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Gui::Gui" ); return 0; } catch(std::exception& e) { pbSetError("Gui::Gui",e.what()); return -1; } } + + void setBackgroundMesh(Mesh* m); static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::setBackgroundMesh"); PyObject *_retval = 0; { ArgLocker _lock; Mesh* m = _args.getPtr<Mesh >("m",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setBackgroundMesh(m); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::setBackgroundMesh"); return _retval; } catch(std::exception& e) { pbSetError("Gui::setBackgroundMesh",e.what()); return 0; } } + void show(bool twoD=false); static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::show"); PyObject *_retval = 0; { ArgLocker _lock; bool twoD = _args.getOpt<bool >("twoD",0,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->show(twoD); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::show"); return _retval; } catch(std::exception& e) { pbSetError("Gui::show",e.what()); return 0; } } + void update(); static PyObject* _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::update"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->update(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::update"); return _retval; } catch(std::exception& e) { pbSetError("Gui::update",e.what()); return 0; } } + void pause(); static PyObject* _W_11 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::pause"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->pause(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::pause"); return _retval; } catch(std::exception& e) { pbSetError("Gui::pause",e.what()); return 0; } } + PbClass* addControl(PbType t); static PyObject* _W_12 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::addControl"); PyObject *_retval = 0; { ArgLocker _lock; PbType t = _args.get<PbType >("t",0,&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->addControl(t)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::addControl"); return _retval; } catch(std::exception& e) { pbSetError("Gui::addControl",e.what()); return 0; } } + void screenshot(std::string filename); static PyObject* _W_13 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::screenshot"); PyObject *_retval = 0; { ArgLocker _lock; std::string filename = _args.get<std::string >("filename",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->screenshot(filename); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::screenshot"); return _retval; } catch(std::exception& e) { pbSetError("Gui::screenshot",e.what()); return 0; } } + + // control display upon startup + void nextRealGrid(); static PyObject* _W_14 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextRealGrid"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextRealGrid(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextRealGrid"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextRealGrid",e.what()); return 0; } } + void nextVec3Grid(); static PyObject* _W_15 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextVec3Grid"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextVec3Grid(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextVec3Grid"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextVec3Grid",e.what()); return 0; } } + void nextParts(); static PyObject* _W_16 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextParts"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextParts(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextParts"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextParts",e.what()); return 0; } } + void nextPdata(); static PyObject* _W_17 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextPdata"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextPdata(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextPdata"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextPdata",e.what()); return 0; } } + void nextMesh(); static PyObject* _W_18 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextMesh"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextMesh(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextMesh"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextMesh",e.what()); return 0; } } + void nextVec3Display(); static PyObject* _W_19 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextVec3Display"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextVec3Display(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextVec3Display"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextVec3Display",e.what()); return 0; } } + void nextMeshDisplay(); static PyObject* _W_20 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextMeshDisplay"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextMeshDisplay(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextMeshDisplay"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextMeshDisplay",e.what()); return 0; } } + void nextPartDisplay(); static PyObject* _W_21 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Gui* pbo = dynamic_cast<Gui*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Gui::nextPartDisplay"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->nextPartDisplay(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Gui::nextPartDisplay"); return _retval; } catch(std::exception& e) { pbSetError("Gui::nextPartDisplay",e.what()); return 0; } } + +protected: + GuiThread* mGuiPtr; MainThread* mMainPtr; public: PbArgs _args;} +#define _C_Gui +; + +} // namespace + +#endif + + + diff --git a/source/blender/python/manta_pp/gui/customctrl.h.reg b/source/blender/python/manta_pp/gui/customctrl.h.reg new file mode 100644 index 00000000000..cdf4d27668f --- /dev/null +++ b/source/blender/python/manta_pp/gui/customctrl.h.reg @@ -0,0 +1,31 @@ +#include "gui/customctrl.h" ++CustomControl^ static const Pb::Register _R_$IDX$ ("CustomControl","CustomControl","PbClass"); template<> const char* Namify<CustomControl >::S = "CustomControl"; +>CustomControl^ ++CustomControl^ static const Pb::Register _R_$IDX$ ("CustomControl","CustomControl",CustomControl::_W_0); ++CustomSlider^ static const Pb::Register _R_$IDX$ ("CustomSlider","Slider","CustomControl"); template<> const char* Namify<CustomSlider >::S = "CustomSlider"; +>CustomSlider^ ++CustomSlider^ static const Pb::Register _R_$IDX$ ("CustomSlider","CustomSlider",CustomSlider::_W_1); ++CustomSlider^ static const Pb::Register _R_$IDX$ ("CustomSlider","get",CustomSlider::_W_2); ++CustomSlider^ static const Pb::Register _R_$IDX$ ("CustomSlider","set",CustomSlider::_W_3); ++CustomCheckbox^ static const Pb::Register _R_$IDX$ ("CustomCheckbox","Checkbox","CustomControl"); template<> const char* Namify<CustomCheckbox >::S = "CustomCheckbox"; +>CustomCheckbox^ ++CustomCheckbox^ static const Pb::Register _R_$IDX$ ("CustomCheckbox","CustomCheckbox",CustomCheckbox::_W_4); ++CustomCheckbox^ static const Pb::Register _R_$IDX$ ("CustomCheckbox","get",CustomCheckbox::_W_5); ++CustomCheckbox^ static const Pb::Register _R_$IDX$ ("CustomCheckbox","set",CustomCheckbox::_W_6); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","Gui","PbClass"); template<> const char* Namify<Gui >::S = "Gui"; +>Gui^ ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","Gui",Gui::_W_7); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","setBackgroundMesh",Gui::_W_8); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","show",Gui::_W_9); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","update",Gui::_W_10); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","pause",Gui::_W_11); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","addControl",Gui::_W_12); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","screenshot",Gui::_W_13); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextRealGrid",Gui::_W_14); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextVec3Grid",Gui::_W_15); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextParts",Gui::_W_16); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextPdata",Gui::_W_17); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextMesh",Gui::_W_18); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextVec3Display",Gui::_W_19); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextMeshDisplay",Gui::_W_20); ++Gui^ static const Pb::Register _R_$IDX$ ("Gui","nextPartDisplay",Gui::_W_21); diff --git a/source/blender/python/manta_pp/gui/customctrl.h.reg.cpp b/source/blender/python/manta_pp/gui/customctrl.h.reg.cpp new file mode 100644 index 00000000000..060ae2e56a8 --- /dev/null +++ b/source/blender/python/manta_pp/gui/customctrl.h.reg.cpp @@ -0,0 +1,48 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "gui/customctrl.h" +namespace Manta { +#ifdef _C_CustomCheckbox + static const Pb::Register _R_0 ("CustomCheckbox","Checkbox","CustomControl"); template<> const char* Namify<CustomCheckbox >::S = "CustomCheckbox"; + static const Pb::Register _R_1 ("CustomCheckbox","CustomCheckbox",CustomCheckbox::_W_4); + static const Pb::Register _R_2 ("CustomCheckbox","get",CustomCheckbox::_W_5); + static const Pb::Register _R_3 ("CustomCheckbox","set",CustomCheckbox::_W_6); +#endif +#ifdef _C_CustomControl + static const Pb::Register _R_4 ("CustomControl","CustomControl","PbClass"); template<> const char* Namify<CustomControl >::S = "CustomControl"; + static const Pb::Register _R_5 ("CustomControl","CustomControl",CustomControl::_W_0); +#endif +#ifdef _C_CustomSlider + static const Pb::Register _R_6 ("CustomSlider","Slider","CustomControl"); template<> const char* Namify<CustomSlider >::S = "CustomSlider"; + static const Pb::Register _R_7 ("CustomSlider","CustomSlider",CustomSlider::_W_1); + static const Pb::Register _R_8 ("CustomSlider","get",CustomSlider::_W_2); + static const Pb::Register _R_9 ("CustomSlider","set",CustomSlider::_W_3); +#endif +#ifdef _C_Gui + static const Pb::Register _R_10 ("Gui","Gui","PbClass"); template<> const char* Namify<Gui >::S = "Gui"; + static const Pb::Register _R_11 ("Gui","Gui",Gui::_W_7); + static const Pb::Register _R_12 ("Gui","setBackgroundMesh",Gui::_W_8); + static const Pb::Register _R_13 ("Gui","show",Gui::_W_9); + static const Pb::Register _R_14 ("Gui","update",Gui::_W_10); + static const Pb::Register _R_15 ("Gui","pause",Gui::_W_11); + static const Pb::Register _R_16 ("Gui","addControl",Gui::_W_12); + static const Pb::Register _R_17 ("Gui","screenshot",Gui::_W_13); + static const Pb::Register _R_18 ("Gui","nextRealGrid",Gui::_W_14); + static const Pb::Register _R_19 ("Gui","nextVec3Grid",Gui::_W_15); + static const Pb::Register _R_20 ("Gui","nextParts",Gui::_W_16); + static const Pb::Register _R_21 ("Gui","nextPdata",Gui::_W_17); + static const Pb::Register _R_22 ("Gui","nextMesh",Gui::_W_18); + static const Pb::Register _R_23 ("Gui","nextVec3Display",Gui::_W_19); + static const Pb::Register _R_24 ("Gui","nextMeshDisplay",Gui::_W_20); + static const Pb::Register _R_25 ("Gui","nextPartDisplay",Gui::_W_21); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/gui/glwidget.cpp b/source/blender/python/manta_pp/gui/glwidget.cpp new file mode 100644 index 00000000000..de9e7b32051 --- /dev/null +++ b/source/blender/python/manta_pp/gui/glwidget.cpp @@ -0,0 +1,311 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT OpenGL widget + * + ******************************************************************************/ + +#include "glwidget.h" +#ifdef __APPLE__ +# include <OpenGL/glu.h> +#else +# include <GL/glu.h> +#endif +#include <cmath> +#include "painter.h" + +namespace Manta { + +GLWidget::GLWidget(QWidget* p): QGLWidget(QGLFormat(QGL::SampleBuffers), p), mRotX(0), mRotY(0), mGridsize(0), mScreenshotNumber(0) +{ + mPlaneDim = 2; // Y plane + mPlane = -1; + mCamPos = Vec3(0, 0, -2); + for (int i=0; i<MoveDirNum; i++) + mMoveState[i] = false; + mMoveFast = false; + + setAutoBufferSwap(true); + setFocusPolicy(Qt::ClickFocus); + startTimer(10); +} + +GLWidget::~GLWidget() +{ + +} + +QSize GLWidget::minimumSizeHint() const +{ + return QSize(400, 300); +} + +QSize GLWidget::sizeHint() const +{ + return QSize(800, 600); +} + +void GLWidget::initializeGL() +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(1.0); +} + +void GLWidget::paintGL() +{ + if (mGridsize.max() == 0) return; + glDepthFunc(GL_ALWAYS); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + //glEnable(GL_POLYGON_OFFSET_FILL); + //glPolygonOffset(0,0); + + // camera transform + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(mCamPos.x, mCamPos.y, mCamPos.z); + glRotatef(mRotX, 1.,0.,0.); + glRotatef(mRotY, 0.,1.,0.); + Real dx = 1.0 / (Real) mGridsize.max(); + Vec3 sz = toVec3(mGridsize) * (-0.5f * dx); + + glTranslatef(sz.x, sz.y, sz.z); + emit paintSub(); +} + +void GLWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, (GLsizei) w, (GLsizei) h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + GLfloat fov = 45; + GLfloat zNear = 0.05f; + GLfloat zFar = 100.0f; + GLfloat aspect = float(w)/float(h); + GLfloat fH = tan( float(fov / 360.0f * M_PI) ) * zNear; + GLfloat fW = fH * aspect; + glFrustum( -fW, fW, -fH, fH, zNear, zFar ); + glMatrixMode(GL_MODELVIEW); + +} + +void GLWidget::mouseReleaseEvent(QMouseEvent* event) { + // only do tooltip if not moving + QPoint pos = event->pos(); + if ((mDownPos - pos).manhattanLength() == 0) { + // get GL transform matrices + int viewport[4]; + GLdouble modelMatrix[16], projMatrix[16]; + glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix); + glGetDoublev(GL_PROJECTION_MATRIX,projMatrix); + glGetIntegerv(GL_VIEWPORT,viewport); + + // obtain click line + GLdouble line[6], wx=pos.x(), wy=viewport[3]-pos.y(); + if (!gluUnProject(wx,wy,0,modelMatrix,projMatrix,viewport,&line[0],&line[1],&line[2])) return; + if (!gluUnProject(wx,wy,1.0,modelMatrix,projMatrix,viewport,&line[3],&line[4],&line[5])) return; + + // calculate intersection with plane + emit clickLine(event->globalPos(), line[0],line[1],line[2],line[3],line[4],line[5]); + } +} + +void GLWidget::mouseMoveEvent(QMouseEvent* e) +{ + const float speedRot = 0.2f, speedPan = 0.002f; + + QPoint diff = e->pos() - mAnchor; + if (e->buttons() & Qt::LeftButton) { + mRotX += diff.y() * speedRot; + mRotY += diff.x() * speedRot; + updateGL(); + } + if (e->buttons() & Qt::RightButton) { + mCamPos.x += diff.x() * speedPan; + mCamPos.y -= diff.y() * speedPan; + updateGL(); + } + + mAnchor = e->pos(); +} + +void GLWidget::mousePressEvent(QMouseEvent* e) +{ + mDownPos = mAnchor = e->pos(); +} + +void GLWidget::wheelEvent(QWheelEvent* e) +{ + const float speed = 0.002f; + mCamPos.z += speed*e->delta(); + updateGL(); +} + +void GLWidget::timerEvent(QTimerEvent* e) +{ + bool doRepaint = false; + + float speed = 0.005f; + if (mMoveFast) speed *= 5.; + + if (mMoveState[MoveLeft]) { mCamPos.x += speed; doRepaint = true; } + if (mMoveState[MoveRight]) { mCamPos.x -= speed; doRepaint = true; } + if (mMoveState[MoveUp]) { mCamPos.y -= speed; doRepaint = true; } + if (mMoveState[MoveDown]) { mCamPos.y += speed; doRepaint = true; } + if (mMoveState[MoveOut]) { mCamPos.z -= speed; doRepaint = true; } + if (mMoveState[MoveIn]) { mCamPos.z += speed; doRepaint = true; } + if (doRepaint) + updateGL(); +} + +void GLWidget::setViewport(const Vec3i& gridsize) { + if (mGridsize.x != gridsize.x || + mGridsize.y != gridsize.y || + mGridsize.z != gridsize.z) { + if (mPlane < 0) { + mPlane = gridsize[mPlaneDim] / 2; + } else { + Real fac = (Real)gridsize[mPlaneDim] / (Real)mGridsize[mPlaneDim]; + mPlane = (int)(fac * mPlane); + } + mGridsize = gridsize; + emit painterEvent(Painter::EventSetMax, mGridsize[mPlaneDim]); + emit painterEvent(Painter::EventSetPlane, mPlane); + } +} + +void GLWidget::keyPressEvent(QKeyEvent* e) +{ + if(!keyProcess(e->key(), e->modifiers(), true)) + QGLWidget::keyPressEvent(e); + else + updateGL(); +} + +void GLWidget::keyReleaseEvent(QKeyEvent* e) +{ + if(!keyProcess(e->key(), e->modifiers(), false)) + QGLWidget::keyReleaseEvent(e); + else + updateGL(); +} + +bool GLWidget::keyProcess(int key, int modifier, bool down) +{ + bool shift = (modifier & Qt::ShiftModifier); + bool alt = (modifier & Qt::AltModifier); + bool ctrl = (modifier & Qt::ControlModifier); + if (key == Qt::Key_A) { mMoveState[MoveLeft] = down; mMoveFast = shift; } + else if (key == Qt::Key_D) { mMoveState[MoveRight] = down; mMoveFast = shift; } + else if (key == Qt::Key_W) { mMoveState[MoveUp] = down; mMoveFast = shift; } + else if (key == Qt::Key_S) { mMoveState[MoveDown] = down; mMoveFast = shift; } + else if (key == Qt::Key_Q) { mMoveState[MoveIn] = down; mMoveFast = shift; } + else if (key == Qt::Key_E) { mMoveState[MoveOut] = down; mMoveFast = shift; } + else if (down) + { + // only press events + // note Key_P and Key_L used for play/step in mainwindow.cpp + if (key == Qt::Key_Z) { /* next "solver" info sometime? */ } + else if (key == Qt::Key_G) { emit painterEvent(Painter::EventToggleGridDisplay); } + // data grids, first int + else if (key == Qt::Key_X && shift) { /* int display mdoes, not yet used */ } + else if (key == Qt::Key_X) { emit painterEvent(Painter::EventNextInt); updatePlane(mPlane); } + // real + else if (key == Qt::Key_C && shift) { emit painterEvent(Painter::EventNextRealDisplayMode); /* real display modes */ } + else if (key == Qt::Key_C) { emit painterEvent(Painter::EventNextReal); updatePlane(mPlane); } + else if ((key == Qt::Key_Less) || + (key == Qt::Key_Comma) ) { emit painterEvent(Painter::EventScaleRealDown); } + else if ((key == Qt::Key_Greater) || + (key == Qt::Key_Period) ) { emit painterEvent(Painter::EventScaleRealUp); } + + // vec3 grids, scaling can be used with two key combinations (the second one is for international keyboards) + else if (key == Qt::Key_V && shift) { emit painterEvent(Painter::EventNextVecDisplayMode); } + else if (key == Qt::Key_V) { emit painterEvent(Painter::EventNextVec); updatePlane(mPlane); } + // grid scaling + else if (key == Qt::Key_Semicolon) { emit painterEvent(Painter::EventScaleVecDown); } + else if (key == Qt::Key_Apostrophe) { emit painterEvent(Painter::EventScaleVecUp); } + + // particles + else if (key == Qt::Key_B && shift) { emit painterEvent(Painter::EventNextParticleDisplayMode); } + else if (key == Qt::Key_B && alt) { emit painterEvent(Painter::EventNextSystem); } + else if (key == Qt::Key_B) { emit painterEvent(Painter::EventToggleParticles); } + + else if((key == Qt::Key_ParenLeft) || // a bit ugly, but for some reason parentheses dont work in some cases... fall back with dual assignment + (key == Qt::Key_9) ) { emit painterEvent(Painter::EventScalePdataDown); } + else if((key == Qt::Key_ParenRight) || + (key == Qt::Key_0) ) { emit painterEvent(Painter::EventScalePdataUp); } + + // mesh display + else if (key == Qt::Key_M && shift) emit painterEvent(Painter::EventMeshMode); + else if (key == Qt::Key_BraceLeft ) { emit painterEvent(Painter::EventScaleMeshDown); } + else if (key == Qt::Key_BracketLeft) { emit painterEvent(Painter::EventScaleMeshDown); } + else if (key == Qt::Key_BraceRight) { emit painterEvent(Painter::EventScaleMeshUp); } + else if (key == Qt::Key_BracketRight) { emit painterEvent(Painter::EventScaleMeshUp); } + // special mesh display modes + else if (key == Qt::Key_M && alt) emit painterEvent(Painter::EventMeshColorMode); + else if (key == Qt::Key_M && ctrl) emit painterEvent(Painter::EventToggleBackgroundMesh); + else if (key == Qt::Key_M) emit painterEvent(Painter::EventNextMesh); + + // switch display planes + else if ( (key == Qt::Key_Asterisk) || (key == Qt::Key_8) ) { + mPlaneDim = (mPlaneDim+1) % 3; + emit painterEvent(Painter::EventSetDim, mPlaneDim); + emit painterEvent(Painter::EventSetMax, mGridsize[mPlaneDim]); + } + // move plane (+shift is faster) + else if (shift && ((key == Qt::Key_Plus) || (key == Qt::Key_Equal)) ) { + updatePlane(mPlane + 10); + } else if (shift && ((key == Qt::Key_Minus) || (key == Qt::Key_Underscore)) ) { + updatePlane(mPlane - 10); + } else if (key == Qt::Key_Plus || key == Qt::Key_Equal) { + updatePlane(mPlane + 1); + } else if (key == Qt::Key_Minus) { + updatePlane(mPlane - 1); + } + else if ( key == Qt::Key_K) { + QString filename = QString("scr_%1.png").arg(QString::number(mScreenshotNumber), 3, QChar('0')); + screenshot(filename); + mScreenshotNumber++; + } + + else return false; + } + else return false; + return true; +} + +void GLWidget::screenshot(QString file) { + grabFrameBuffer().save(file); +} + +void GLWidget::updatePlane(int plane) { + mPlane = clamp(plane, 0, mGridsize[mPlaneDim]); + emit painterEvent(Painter::EventSetPlane, mPlane); +} + + + +} + + diff --git a/source/blender/python/manta_pp/gui/glwidget.h b/source/blender/python/manta_pp/gui/glwidget.h new file mode 100644 index 00000000000..ee034c317b1 --- /dev/null +++ b/source/blender/python/manta_pp/gui/glwidget.h @@ -0,0 +1,87 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT OpenGL widget + * + ******************************************************************************/ + +#ifndef _GLWIDGET_H__ +#define _GLWIDGET_H__ + +#include <QGLWidget> +#include <QtOpenGL> +#include "vectorbase.h" + +namespace Manta { + +class GLWidget : public QGLWidget { +Q_OBJECT + +public: + GLWidget(QWidget *parent = NULL); + ~GLWidget(); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void wheelEvent(QWheelEvent *e); + void screenshot(QString file); + + void setCamPos(Vec3 pos) { mCamPos = pos; } + +public slots: + void setViewport(const Vec3i& gridsize); + void keyPressEvent(QKeyEvent* e); + void keyReleaseEvent(QKeyEvent* e); + +signals: + void paintSub(); + void clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2); + void painterEvent(int e, int param=0); + +protected: + bool keyProcess(int key, int mod, bool down); + void timerEvent(QTimerEvent* e); + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + void updatePlane(int plane); + + enum MoveDir { None = 0, MoveLeft, MoveRight, MoveUp, MoveDown, MoveIn, MoveOut, MoveDirNum }; + + bool mMoveState[MoveDirNum]; + bool mMoveFast; + QPoint mAnchor, mDownPos; + Vec3 mCamPos; + float mRotX, mRotY; + Vec3i mGridsize; + int mPlaneDim, mPlane; + + int mScreenshotNumber; +}; + +} // namespace + +#endif + + diff --git a/source/blender/python/manta_pp/gui/glwidget.h.reg b/source/blender/python/manta_pp/gui/glwidget.h.reg new file mode 100644 index 00000000000..d0d8ed4b59d --- /dev/null +++ b/source/blender/python/manta_pp/gui/glwidget.h.reg @@ -0,0 +1 @@ +#include "gui/glwidget.h" diff --git a/source/blender/python/manta_pp/gui/glwidget.h.reg.cpp b/source/blender/python/manta_pp/gui/glwidget.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/glwidget.h.reg.cpp diff --git a/source/blender/python/manta_pp/gui/mainwindow.cpp b/source/blender/python/manta_pp/gui/mainwindow.cpp new file mode 100644 index 00000000000..62d2f69f376 --- /dev/null +++ b/source/blender/python/manta_pp/gui/mainwindow.cpp @@ -0,0 +1,318 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT main window + * + ******************************************************************************/ + +#include "mainwindow.h" +#include "qtmain.h" + +#include <QLabel> +#include <QMenu> +#include <QMenuBar> +#include <QAction> +#include <QtOpenGL> +#include <sstream> +#include "meshpainter.h" +#include "particlepainter.h" + +using namespace std; + +namespace Manta { + +MainWnd::MainWnd() : QMainWindow(0), mPaused(true), mRequestPause(false), mRequestClose(false), mStep(0), + mKbwScene(0), mKbwView(0), mKbwPixmap(0), mMenuBar(0) +{ + // Frame info label + mInfo = new QLabel; + setStep(0); + + // register GL widget + mGlWidget = new GLWidget(); + setCentralWidget(mGlWidget); + connect(mGlWidget, SIGNAL(clickLine(QPoint,float,float,float,float,float,float)), SLOT(clickLine(QPoint,float,float,float,float,float,float))); + + // register grid painters + mPainterLayout = new QVBoxLayout; + mPainterLayout->setAlignment(Qt::AlignTop); + mPainterLayout->addWidget(mInfo); + GridPainter<int>* intPainter = new GridPainter<int>(NULL, this); + mPainter.push_back(new GridPainter<Real>((FlagGrid**)intPainter->getGridPtr(), this)); + mPainter.push_back(new GridPainter<Vec3>(NULL, this)); + mPainter.push_back(intPainter); + mPainter.push_back(new ParticlePainter(intPainter, this)); + MeshPainter* ptr = new MeshPainter(this); + mPainter.push_back(ptr); + connect(this, SIGNAL(setBackgroundMesh(Mesh*)), ptr, SLOT(setBackgroundMesh(Mesh*))); + + for (int i=0; i<(int)mPainter.size(); i++) { + connect(mGlWidget, SIGNAL(paintSub()), mPainter[i], SLOT(paint())); + connect(mGlWidget, SIGNAL(painterEvent(int, int)), mPainter[i], SLOT(doEvent(int, int))); + connect(this, SIGNAL(painterEvent(int, int)), mPainter[i], SLOT(doEvent(int, int))); + connect(mPainter[i], SIGNAL(setViewport(const Vec3i&)), mGlWidget, SLOT(setViewport(const Vec3i&))); + mPainter[i]->attachWidget(mPainterLayout); + } + + // docking widget for painters + QDockWidget* painterDock = new QDockWidget("Info", this); + QWidget* painterProxy = new QWidget; + painterProxy->setLayout(mPainterLayout); + painterDock->setWidget(painterProxy); + painterDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + addDockWidget(Qt::RightDockWidgetArea, painterDock); + + // Top toolbar + QToolBar* toolbar = addToolBar("Control"); + toolbar->setAllowedAreas(Qt::TopToolBarArea); + toolbar->setMovable(false); + mAcPlay = toolbar->addAction(QIcon(":/play.png"),"Play"); + mAcPlay->setStatusTip("Continue simulation"); + connect(mAcPlay, SIGNAL(triggered()), SLOT(play())); + mAcPause = toolbar->addAction(QIcon(":/pause.png"),"Pause"); + mAcPause->setStatusTip("Pause simulation"); + connect(mAcPause, SIGNAL(triggered()), SLOT(pause())); + emit play(); + + // build menu + /*QAction* a = new QAction(this); + a->setText( "Quit" ); + connect(a, SIGNAL(triggered()), SLOT(close()) ); + mMenuBar = menuBar()->addMenu( "File" ); + mMenuBar->addAction( a ); */ + + // keyboard info window, show on demand + mKbwScene = new QGraphicsScene(); + mKbwView = new QGraphicsView(mKbwScene); + mKbwPixmap = new QGraphicsPixmapItem(QPixmap(":/keyboard.png")); + mKbwScene->addItem(mKbwPixmap); + mKbwView->hide(); + + mAcHelp = toolbar->addAction(QIcon(":/help.png"),"Help"); + mAcHelp->setStatusTip("Help"); + connect(mAcHelp, SIGNAL(triggered()), SLOT(showHelp())); + + // start... + mGlWidget->setFocus(); + this->raise(); + this->activateWindow(); + + /* + // MLE 2014-07-05 added + // move gui window to upper left corner and resize window to screen size + QRect rc = frameGeometry(); + QRect rcDesktop = QApplication::desktop()->frameGeometry(); + rc.setLeft(rcDesktop.left()); + rc.setTop(rcDesktop.top()); + rc.setRight(rcDesktop.right()); + rc.setBottom(rcDesktop.bottom()); + move(rc.topLeft()); + resize(rc.size()); + */ + + // uncomment to start paused + //emit pause(); +} + +void MainWnd::clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2) { + string msg; + for (int i=mPainter.size()-1; i>=0; i--) { + msg += mPainter[i]->clickLine(Vec3(p0,p1,p2),Vec3(q0,q1,q2)); + } + if (!msg.empty()) + QToolTip::showText(pos, QString(msg.c_str())); +} + +void MainWnd::addControl(void* ctrl) { + CustomControl* control = (CustomControl*) ctrl; + mCtrls.push_back(control); + control->init(mPainterLayout); +} + +void MainWnd::setStep(int f) { + std::stringstream s; + s << "Simulation step " << f; + mInfo->setText(s.str().c_str()); +} + +void MainWnd::setPauseStatus(bool v) +{ + mPaused = v; +} + +bool MainWnd::event(QEvent* e) { + if (e->type() == (QEvent::Type)EventGuiShow) { + if (!mRequestClose) { + this->show(); + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventFullUpdate) { + if (!mRequestClose) { + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventStepUpdate) { + if (!mRequestClose) { + if (mRequestPause) + emit painterEvent(Painter::UpdateFull); + else + emit painterEvent(Painter::UpdateStep); + mGlWidget->updateGL(); + } + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventFinalUpdate) { + if (!mRequestClose) { + emit painterEvent(Painter::UpdateFull); + mGlWidget->updateGL(); + } + mRequestClose = true; + emit wakeMain(); + return true; + } + else if (e->type() == (QEvent::Type)EventSet2DCam) { + mGlWidget->setCamPos( Vec3(0, 0, -1.3) ); + return true; + } + else if (e->type() == (QEvent::Type)EventInstantKill) { + emit killMain(); + emit exitApp(); + return true; + } + + // update button states for pause events + if( (mRequestPause) && (!mAcPlay->isEnabled()) ) { + mAcPlay->setEnabled(true); + mAcPause->setEnabled(false); + } + if( (mRequestPause) && (!mAcPlay->isEnabled()) ) { + mAcPlay->setEnabled(true); + mAcPause->setEnabled(false); + } + + return QMainWindow::event(e); +} + +void MainWnd::keyPressEvent(QKeyEvent* e) { + if (e->key() == Qt::Key_Escape) { + mRequestClose = true; + emit killMain(); + this->close(); + } else if (e->key() == Qt::Key_Space) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else { + emit painterEvent(mPaused ? Painter::UpdateFull : Painter::UpdateRequest); + mGlWidget->updateGL(); + } + } else if (e->key() == Qt::Key_P) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else if (mRequestPause) + emit play(); + else + emit pause(); + } else if (e->key() == Qt::Key_L) { + if (mRequestClose) { + emit killMain(); + this->close(); + } else if (mRequestPause) { + mRequestPause = false; + mStep = (e->modifiers() & Qt::ShiftModifier) ? 1 : 2; + } else + emit pause(); + } else if (e->key() == Qt::Key_H) { + emit showHelp(); + } else { + mGlWidget->keyPressEvent(e); // let gl widget take care of keyboard shortcuts + //QMainWindow::keyPressEvent(e); + } +} +void MainWnd::keyReleaseEvent(QKeyEvent* e) +{ + mGlWidget->keyReleaseEvent(e); +} + +void MainWnd::pause() { + mRequestPause = true; + // dont call: mAcPlay/mAcPause ->setEnabled(true) here; wrong thread if called from python +} + +void MainWnd::play() { + mRequestPause = false; + mAcPlay->setEnabled(false); + mAcPause->setEnabled(true); +} + +void MainWnd::step() { + mStep = 2; + mRequestPause = false; +} + +void MainWnd::showHelp() { + mKbwView->show(); +} + +void MainWnd::nextRealGrid() { + emit painterEvent(Painter::EventNextReal); +} +void MainWnd::nextVec3Grid() { + emit painterEvent(Painter::EventNextVec); +} +void MainWnd::nextMesh() { + emit painterEvent(Painter::EventNextMesh); +} +void MainWnd::nextParts() { + emit painterEvent(Painter::EventNextSystem); +} +void MainWnd::nextPdata() { + emit painterEvent(Painter::EventToggleParticles); +} +void MainWnd::nextVec3Display() { + emit painterEvent(Painter::EventNextVecDisplayMode); +} +void MainWnd::nextPartDisplay() { + emit painterEvent(Painter::EventNextParticleDisplayMode); +} +void MainWnd::nextMeshDisplay() { + emit painterEvent(Painter::EventMeshMode); +} + +MainWnd::~MainWnd() { +} + +void MainWnd::screenshot(QString file) { + mGlWidget->screenshot(file); +} + + +} + + diff --git a/source/blender/python/manta_pp/gui/mainwindow.h b/source/blender/python/manta_pp/gui/mainwindow.h new file mode 100644 index 00000000000..a760461b4b7 --- /dev/null +++ b/source/blender/python/manta_pp/gui/mainwindow.h @@ -0,0 +1,106 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT main window + * + ******************************************************************************/ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include <QMainWindow> +#include <QMenuBar> +#include "glwidget.h" +#include "customctrl.h" +#include "painter.h" +#include <vector> + +namespace Manta { +class Mesh; + +class MainWnd : public QMainWindow +{ +Q_OBJECT +public: + enum EventType { EventFullUpdate = QEvent::User, EventGuiShow, + EventStepUpdate, EventFinalUpdate, EventInstantKill, EventSet2DCam }; + + MainWnd(); + virtual ~MainWnd(); + bool event(QEvent* e); + void keyPressEvent(QKeyEvent* e); + void keyReleaseEvent(QKeyEvent* e); + inline bool pauseRequest() { return mRequestPause && !mRequestClose; } + inline bool closeRequest() { return mRequestClose; } + void setPauseStatus(bool v); + void stepReset(bool fullUpdate) { if (mStep == 1 || (mStep == 2 && fullUpdate)) {mRequestPause = true; mStep = 0;} } + void requestClose() { mRequestClose =true; } + void setStep(int f); + void setBackground(Mesh *m) { emit setBackgroundMesh(m); } + +public slots: + void pause(); + void play(); + void step(); + void showHelp(); + void addControl(void* ctrl); + void screenshot(QString file); + void clickLine(QPoint pos, float p0, float p1,float p2, float q0, float q1, float q2); + + void nextRealGrid(); + void nextVec3Grid(); + void nextMesh(); + void nextParts(); + void nextPdata(); + void nextVec3Display(); + void nextPartDisplay(); + void nextMeshDisplay(); + +signals: + void painterEvent(int e, int param=0); + void wakeMain(); + void setBackgroundMesh(Mesh* bgr); + void killMain(); + void exitApp(); + +protected: + bool mPaused, mRequestPause, mRequestClose; + int mStep; + GLWidget *mGlWidget; + QAction *mAcPlay, *mAcPause; + std::vector<Painter*> mPainter; + std::vector<CustomControl*> mCtrls; + QLabel* mInfo; + QVBoxLayout* mPainterLayout; + + // keyboard info window + QAction *mAcHelp; + QGraphicsScene *mKbwScene; + QGraphicsView *mKbwView; + QGraphicsPixmapItem *mKbwPixmap; + + QMenu *mMenuBar; +}; + +} + +#endif + + diff --git a/source/blender/python/manta_pp/gui/mainwindow.h.reg b/source/blender/python/manta_pp/gui/mainwindow.h.reg new file mode 100644 index 00000000000..b71e6628a99 --- /dev/null +++ b/source/blender/python/manta_pp/gui/mainwindow.h.reg @@ -0,0 +1 @@ +#include "gui/mainwindow.h" diff --git a/source/blender/python/manta_pp/gui/mainwindow.h.reg.cpp b/source/blender/python/manta_pp/gui/mainwindow.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/mainwindow.h.reg.cpp diff --git a/source/blender/python/manta_pp/gui/meshpainter.cpp b/source/blender/python/manta_pp/gui/meshpainter.cpp new file mode 100644 index 00000000000..5f04cbc7152 --- /dev/null +++ b/source/blender/python/manta_pp/gui/meshpainter.cpp @@ -0,0 +1,313 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting mesh objects + * + ******************************************************************************/ + +#include "meshpainter.h" +#include <QtOpenGL> +#include "mesh.h" +#include "vortexsheet.h" + +using namespace std; + +namespace Manta { + +MeshPainter::MeshPainter(QWidget* par) + : LockedObjPainter(par), mColorScale(1.0), mMode(ModeFlatShade), mVorticityMode(VModeNone), + mBackgroundMode(BModeNormal), mLocalMesh(0) , mBackground(0), mHide(false) +{ + mInfo = new QLabel(); +} + +MeshPainter::~MeshPainter() { + if (mLocalMesh) + delete mLocalMesh; +} + +void MeshPainter::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +void MeshPainter::update() { + Mesh* src = (Mesh*) mObject; + + // always reallocate + if (mLocalMesh) + delete mLocalMesh; + + mLocalMesh = src->clone(); + + updateText(); +} + +string MeshPainter::getID() { return "Mesh"; } + + +void MeshPainter::processKeyEvent(PainterEvent e, int param) +{ + if (e == EventNextMesh) + nextObject(); + else if (e == EventMeshMode) + mMode = (DisplayMode) ((mMode+1) % (int)Num_DisplayModes); + else if (e == EventScaleMeshUp) + mColorScale *= 2.0; + else if (e == EventScaleMeshDown) + mColorScale /= 2.0; + else if (e == EventMeshColorMode) + mVorticityMode = (VorticityMode) ((mVorticityMode+1)%(int)Num_VorticityModes); + else if (e == EventToggleBackgroundMesh) + mBackgroundMode = (BackgroundMode) ((mBackgroundMode+1)%(int)Num_BackgroundModes); + else return; + + updateText(); +} + +void MeshPainter::updateText() { + stringstream s; + + if ( mObject && !mHide && mLocalMesh ) { + s << "Mesh '" << mLocalMesh->getName() << "' [" << mLocalMesh->numTris() << " tris]" << endl; + if (mMode == ModeFlatShade) s << "DisplayMode: Flatshade" << endl; + if (mMode == ModeInvisible) s << "DisplayMode: Invisible" << endl; + if (mMode == ModeLines) s << "DisplayMode: Wireframe" << endl; + if (mMode == ModePoints) s << "DisplayMode: Pointset" << endl; + if (mMode == ModeTrans) s << "DisplayMode: Semi-Transparent" << endl; + if (mLocalMesh->getType() == Mesh::TypeVortexSheet) { + if (mVorticityMode==VModeFull) s << "Vorticity: full" << endl; + if (mVorticityMode==VModeSmoothed) s << "Vorticity: gauss smooth" << endl; + if (mVorticityMode==VModeDiff) s << "Vorticity: gauss diff" << endl; + if (mVorticityMode==VModeSmoke) s << "Smoke Density" << endl; + if (mVorticityMode==VModeTex) s << "Texture coordinates 0" << endl; + if (mVorticityMode!=VModeNone) s << " Scale " << 1.0/mColorScale << "" << endl; + } + } + mInfo->setText(s.str().c_str()); +} + +void MeshPainter::setBackgroundMesh(Mesh* bgr) { + mBackground = bgr; +} + +void MeshPainter::setupLights(bool specular) { + // control colors + float max = 1.0; + float dim = 0.5; + float dims = specular ? dim : 0; + float maxs = specular ? max : 0; + float amb = 0.1; + float shininess = 50.; + dim = 0.5; max = 0.75; amb = 0.25; + + float ambient0[] = {amb, amb, amb, max}; + glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0); + + float diffuse0[] = {max, dim, dim, 1.0}; + float specular0[] = {maxs, dims, dims, 1.0}; + float position0[] = { 5., 5., 5., 1.0f }; + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0); + glLightfv(GL_LIGHT0, GL_SPECULAR, specular0); + glLightfv(GL_LIGHT0, GL_POSITION, position0); + + float diffuse1[] = {dim, max, dim, 1.0}; + float specular1[] = {dims, maxs, dims, 1.0}; + float position1[] = { 5., -5., -5., 1.0f }; + glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1); + glLightfv(GL_LIGHT1, GL_SPECULAR, specular1); + glLightfv(GL_LIGHT1, GL_POSITION, position1); + + float diffuse2[] = {dim, dim, max, 1.0}; + float specular2[] = {dims, dims, maxs, 1.0}; + float position2[] = { 0.3, 2., -10., 1.0f }; + glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2); + glLightfv(GL_LIGHT2, GL_SPECULAR, specular2); + glLightfv(GL_LIGHT2, GL_POSITION, position2); + + float specReflection[] = { dims, dims, dims, 1.0f }; + if(shininess == 0.) specReflection[0] = specReflection[1] = specReflection[2] = 0.; + glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); + glMateriali(GL_FRONT, GL_SHININESS, shininess); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHT2); +} + +//static inline void glColor(const Vec3& color) { + //glColor3f(std::max(_0,std::min(_1,color.x)), std::max(_0,std::min(_1,color.y)), std::max(_0,std::min(_1,color.z))); +//} + +static inline void glVertex(const Vec3& v, Real dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} +static inline void glNormal(const Vec3& v) { + glNormal3f(v.x, v.y, v.z); +} + +void MeshPainter::paint() { + if (!mObject || mHide || !mLocalMesh) return; + + Real dx = mLocalMesh->getParent()->getDx(); + + bool triColor = (mMode == ModeFlatShade) && (mLocalMesh->getType() == Mesh::TypeVortexSheet) && (mVorticityMode!=VModeNone); + bool nodeColor = (mLocalMesh->getType() == Mesh::TypeVortexSheet) && (mVorticityMode==VModeTex); + + // setup OpenGL lighting and material + const float isoAlpha = 0.4; + setupLights(false); + glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ; + glEnable(GL_COLOR_MATERIAL); + glDepthFunc(GL_LESS); + //glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + // draw background + if (mBackground != NULL && mBackgroundMode != BModeInvisible) { + if (mBackgroundMode != BModeTrans) { + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glColor3f(0.3,0.3,0.5); + } else { + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glColor4f(0.6,0.6,0.8,0.5); + } + glEnable(GL_CULL_FACE); + glPolygonOffset(1.0, 0.5); + glBegin(GL_TRIANGLES); + + for(int tri=0; tri<mBackground->numTris(); tri++) { + Vec3 normal = mBackground->getFaceNormal(tri); + for (int c=0; c<3; c++) { + glNormal(normal); + glVertex(mBackground->getNode(tri,c), dx); + } + } + glEnd(); + glPolygonOffset(1., 1.); + glDisable(GL_CULL_FACE); + } + + setupLights(true); + if (mMode == ModeFlatShade) { + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } else { + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + } + if (triColor) + glDisable(GL_LIGHTING); + + // draw triangles + if (mMode == ModeFlatShade || mMode == ModeTrans) + { + glEnable(GL_CULL_FACE); + glPolygonOffset(1.0, 0.5); + glBegin(GL_TRIANGLES); + + const int numTris = (int)mLocalMesh->numTris(); + for(int tri=0; tri<numTris; tri++) { + if (!nodeColor && triColor) { + VortexSheetInfo& info = ((VortexSheetMesh*)mLocalMesh)->sheet(tri); + Vec3 v = info.vorticity; + if (mVorticityMode == VModeSmoothed) v = info.vorticitySmoothed; + if (mVorticityMode == VModeDiff) v -= info.vorticitySmoothed; + if (mVorticityMode == VModeSmoke) v = info.smokeAmount / 20.0f; + + Vec3 ca = v * 20.0 * mColorScale; + Vec3 color = Vec3(fabs(ca.x),fabs(ca.y),fabs(ca.z)); + glColor3f(color.x, color.y, color.z); + } else if (mLocalMesh->isTriangleFixed(tri)) + glColor3f(0,1,0); + else if (mLocalMesh->tris(tri).flags & Mesh::FfMarked) + glColor3f(1,0,0); + else + glColor4f(0.5,0.5,0.5, isoAlpha); + + for (int c=0; c<3; c++) { + if (nodeColor) { + Vec3 tc = ((VortexSheetMesh*)mLocalMesh)->tex1(mLocalMesh->tris(tri).c[c]); + //Vec3 tc2 = ((VortexSheetMesh*)mLocalMesh)->tex2(mLocalMesh->tris(tri).c[c]); + //Vec3 tc = gAlpha*tc1+(1-gAlpha)*tc2; + tc = mColorScale * (tc / toVec3(mLocalMesh->getParent()->getGridSize())); + tc = nmod(tc, Vec3(1,1,1)); + glColor3f(tc.x, tc.y ,tc.z); + } + glNormal(mLocalMesh->getFaceNormal(tri)); + glVertex(mLocalMesh->getNode(tri,c), dx); + } + } + glEnd(); + glPolygonOffset(1., 1.); + glDisable(GL_CULL_FACE); + } + + // Disable light setup + glDisable(GL_BLEND); + glDisable(GL_LIGHT0); + glDisable(GL_LIGHT1); + glDisable(GL_LIGHT2); + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_TEXTURE_2D); + + // draw mesh lines + if(mMode == ModeLines) { + glColor3d(1.0, 0.9, 0.9); + glLineWidth(1.0); + glBegin(GL_LINES); + const int numTris = (int)mLocalMesh->numTris(); + for(int tri=0; tri<numTris; tri++) + for (int j=5; j<5+6; j++) + glVertex( mLocalMesh->getNode(tri,(j/2)%3), dx); + glEnd(); + } + + // draw vertex points + if(mMode == ModePoints) { + static const Vec3 colorSpecial (0.3, 0.5, 0.2); + //static const Vec3 colortable[] = { Vec3(0.5), Vec3(1,0,0), Vec3(0,1,0), Vec3(0,0,1) }; + + glPointSize(2.0); + glBegin(GL_POINTS); + const int numNodes = (int)mLocalMesh->numNodes(); + for(int i=0; i<numNodes; i++) { + Vec3 color(0.5, 0.5, 0.5); + if (mLocalMesh->isNodeFixed(i)) + color = Vec3(0,1,0); + else if (mLocalMesh->nodes(i).flags & Mesh::NfMarked) + color = Vec3(1,0,0); + //int flags = mLocalMesh->flags(i); + + glColor3f(color.x, color.y, color.z); + glVertex(mLocalMesh->nodes(i).pos, dx); + } + glEnd(); + glPointSize(1.0); + } +} + + + +} // namespace + + diff --git a/source/blender/python/manta_pp/gui/meshpainter.h b/source/blender/python/manta_pp/gui/meshpainter.h new file mode 100644 index 00000000000..f11115c3c4f --- /dev/null +++ b/source/blender/python/manta_pp/gui/meshpainter.h @@ -0,0 +1,71 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting mesh objects + * + ******************************************************************************/ + +#ifndef _MESHPAINTER_H_ +#define _MESHPAINTER_H_ + +#include "painter.h" + +namespace Manta { +// fwd decl +class Mesh; + +//! Painter object for Meshes +class MeshPainter : public LockedObjPainter { + Q_OBJECT +public: + enum DisplayMode { ModeTrans=0, ModeLines, ModePoints, ModeFlatShade, ModeInvisible, Num_DisplayModes }; + enum BackgroundMode { BModeNormal=0, BModeTrans, BModeInvisible, Num_BackgroundModes }; + enum VorticityMode { VModeFull=0, VModeSmoothed, VModeDiff, VModeSmoke, VModeTex, VModeNone, Num_VorticityModes }; + + MeshPainter(QWidget* par = 0); + ~MeshPainter(); + + void paint(); + void attachWidget(QLayout* layout); + +public slots: + void setBackgroundMesh(Mesh* bgr); + +protected: + std::string getID(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + void processSpecificKeyEvent(PainterEvent e, int param); + void setupLights(bool specular); + + Real mColorScale; + DisplayMode mMode; + VorticityMode mVorticityMode; + BackgroundMode mBackgroundMode; + Mesh* mLocalMesh, *mBackground; + QLabel* mInfo; + bool mHide; +}; + +} // namespace + +#endif + diff --git a/source/blender/python/manta_pp/gui/meshpainter.h.reg b/source/blender/python/manta_pp/gui/meshpainter.h.reg new file mode 100644 index 00000000000..33f65359335 --- /dev/null +++ b/source/blender/python/manta_pp/gui/meshpainter.h.reg @@ -0,0 +1 @@ +#include "gui/meshpainter.h" diff --git a/source/blender/python/manta_pp/gui/meshpainter.h.reg.cpp b/source/blender/python/manta_pp/gui/meshpainter.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/meshpainter.h.reg.cpp diff --git a/source/blender/python/manta_pp/gui/moc_customctrl.cxx b/source/blender/python/manta_pp/gui/moc_customctrl.cxx new file mode 100644 index 00000000000..9c486274f23 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_customctrl.cxx @@ -0,0 +1,165 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'customctrl.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "customctrl.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'customctrl.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__TextCheckbox[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 23, 21, 20, 20, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__TextCheckbox[] = { + "Manta::TextCheckbox\0\0v\0update(int)\0" +}; + +void Manta::TextCheckbox::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + TextCheckbox *_t = static_cast<TextCheckbox *>(_o); + switch (_id) { + case 0: _t->update((*reinterpret_cast< int(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::TextCheckbox::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::TextCheckbox::staticMetaObject = { + { &QCheckBox::staticMetaObject, qt_meta_stringdata_Manta__TextCheckbox, + qt_meta_data_Manta__TextCheckbox, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::TextCheckbox::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::TextCheckbox::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::TextCheckbox::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__TextCheckbox)) + return static_cast<void*>(const_cast< TextCheckbox*>(this)); + return QCheckBox::qt_metacast(_clname); +} + +int Manta::TextCheckbox::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QCheckBox::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Manta__TextSlider[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 21, 19, 18, 18, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__TextSlider[] = { + "Manta::TextSlider\0\0v\0update(int)\0" +}; + +void Manta::TextSlider::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + TextSlider *_t = static_cast<TextSlider *>(_o); + switch (_id) { + case 0: _t->update((*reinterpret_cast< int(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::TextSlider::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::TextSlider::staticMetaObject = { + { &QSlider::staticMetaObject, qt_meta_stringdata_Manta__TextSlider, + qt_meta_data_Manta__TextSlider, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::TextSlider::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::TextSlider::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::TextSlider::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__TextSlider)) + return static_cast<void*>(const_cast< TextSlider*>(this)); + return QSlider::qt_metacast(_clname); +} + +int Manta::TextSlider::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QSlider::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_customctrl.cxx_parameters b/source/blender/python/manta_pp/gui/moc_customctrl.cxx_parameters new file mode 100644 index 00000000000..36b0887518f --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_customctrl.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_customctrl.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/customctrl.h diff --git a/source/blender/python/manta_pp/gui/moc_glwidget.cxx b/source/blender/python/manta_pp/gui/moc_glwidget.cxx new file mode 100644 index 00000000000..a577cd692c9 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_glwidget.cxx @@ -0,0 +1,132 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'glwidget.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "glwidget.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'glwidget.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__GLWidget[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 7, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 4, // signalCount + + // signals: signature, parameters, type, tag, flags + 17, 16, 16, 16, 0x05, + 50, 28, 16, 16, 0x05, + 112, 104, 16, 16, 0x05, + 136, 134, 16, 16, 0x25, + + // slots: signature, parameters, type, tag, flags + 163, 154, 16, 16, 0x0a, + 182, 134, 16, 16, 0x0a, + 208, 134, 16, 16, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__GLWidget[] = { + "Manta::GLWidget\0\0paintSub()\0" + "pos,p0,p1,p2,q0,q1,q2\0" + "clickLine(QPoint,float,float,float,float,float,float)\0" + "e,param\0painterEvent(int,int)\0e\0" + "painterEvent(int)\0gridsize\0" + "setViewport(Vec3i)\0keyPressEvent(QKeyEvent*)\0" + "keyReleaseEvent(QKeyEvent*)\0" +}; + +void Manta::GLWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + GLWidget *_t = static_cast<GLWidget *>(_o); + switch (_id) { + case 0: _t->paintSub(); break; + case 1: _t->clickLine((*reinterpret_cast< QPoint(*)>(_a[1])),(*reinterpret_cast< float(*)>(_a[2])),(*reinterpret_cast< float(*)>(_a[3])),(*reinterpret_cast< float(*)>(_a[4])),(*reinterpret_cast< float(*)>(_a[5])),(*reinterpret_cast< float(*)>(_a[6])),(*reinterpret_cast< float(*)>(_a[7]))); break; + case 2: _t->painterEvent((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 3: _t->painterEvent((*reinterpret_cast< int(*)>(_a[1]))); break; + case 4: _t->setViewport((*reinterpret_cast< const Vec3i(*)>(_a[1]))); break; + case 5: _t->keyPressEvent((*reinterpret_cast< QKeyEvent*(*)>(_a[1]))); break; + case 6: _t->keyReleaseEvent((*reinterpret_cast< QKeyEvent*(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::GLWidget::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::GLWidget::staticMetaObject = { + { &QGLWidget::staticMetaObject, qt_meta_stringdata_Manta__GLWidget, + qt_meta_data_Manta__GLWidget, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::GLWidget::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::GLWidget::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::GLWidget::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__GLWidget)) + return static_cast<void*>(const_cast< GLWidget*>(this)); + return QGLWidget::qt_metacast(_clname); +} + +int Manta::GLWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QGLWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 7) + qt_static_metacall(this, _c, _id, _a); + _id -= 7; + } + return _id; +} + +// SIGNAL 0 +void Manta::GLWidget::paintSub() +{ + QMetaObject::activate(this, &staticMetaObject, 0, 0); +} + +// SIGNAL 1 +void Manta::GLWidget::clickLine(QPoint _t1, float _t2, float _t3, float _t4, float _t5, float _t6, float _t7) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)), const_cast<void*>(reinterpret_cast<const void*>(&_t5)), const_cast<void*>(reinterpret_cast<const void*>(&_t6)), const_cast<void*>(reinterpret_cast<const void*>(&_t7)) }; + QMetaObject::activate(this, &staticMetaObject, 1, _a); +} + +// SIGNAL 2 +void Manta::GLWidget::painterEvent(int _t1, int _t2) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) }; + QMetaObject::activate(this, &staticMetaObject, 2, _a); +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_glwidget.cxx_parameters b/source/blender/python/manta_pp/gui/moc_glwidget.cxx_parameters new file mode 100644 index 00000000000..98948164723 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_glwidget.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_glwidget.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/glwidget.h diff --git a/source/blender/python/manta_pp/gui/moc_mainwindow.cxx b/source/blender/python/manta_pp/gui/moc_mainwindow.cxx new file mode 100644 index 00000000000..1ab1bc4bb6a --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_mainwindow.cxx @@ -0,0 +1,175 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'mainwindow.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "mainwindow.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'mainwindow.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__MainWnd[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 21, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 6, // signalCount + + // signals: signature, parameters, type, tag, flags + 24, 16, 15, 15, 0x05, + 48, 46, 15, 15, 0x25, + 66, 15, 15, 15, 0x05, + 81, 77, 15, 15, 0x05, + 106, 15, 15, 15, 0x05, + 117, 15, 15, 15, 0x05, + + // slots: signature, parameters, type, tag, flags + 127, 15, 15, 15, 0x0a, + 135, 15, 15, 15, 0x0a, + 142, 15, 15, 15, 0x0a, + 149, 15, 15, 15, 0x0a, + 165, 160, 15, 15, 0x0a, + 188, 183, 15, 15, 0x0a, + 230, 208, 15, 15, 0x0a, + 284, 15, 15, 15, 0x0a, + 299, 15, 15, 15, 0x0a, + 314, 15, 15, 15, 0x0a, + 325, 15, 15, 15, 0x0a, + 337, 15, 15, 15, 0x0a, + 349, 15, 15, 15, 0x0a, + 367, 15, 15, 15, 0x0a, + 385, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__MainWnd[] = { + "Manta::MainWnd\0\0e,param\0painterEvent(int,int)\0" + "e\0painterEvent(int)\0wakeMain()\0bgr\0" + "setBackgroundMesh(Mesh*)\0killMain()\0" + "exitApp()\0pause()\0play()\0step()\0" + "showHelp()\0ctrl\0addControl(void*)\0" + "file\0screenshot(QString)\0pos,p0,p1,p2,q0,q1,q2\0" + "clickLine(QPoint,float,float,float,float,float,float)\0" + "nextRealGrid()\0nextVec3Grid()\0nextMesh()\0" + "nextParts()\0nextPdata()\0nextVec3Display()\0" + "nextPartDisplay()\0nextMeshDisplay()\0" +}; + +void Manta::MainWnd::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + MainWnd *_t = static_cast<MainWnd *>(_o); + switch (_id) { + case 0: _t->painterEvent((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 1: _t->painterEvent((*reinterpret_cast< int(*)>(_a[1]))); break; + case 2: _t->wakeMain(); break; + case 3: _t->setBackgroundMesh((*reinterpret_cast< Mesh*(*)>(_a[1]))); break; + case 4: _t->killMain(); break; + case 5: _t->exitApp(); break; + case 6: _t->pause(); break; + case 7: _t->play(); break; + case 8: _t->step(); break; + case 9: _t->showHelp(); break; + case 10: _t->addControl((*reinterpret_cast< void*(*)>(_a[1]))); break; + case 11: _t->screenshot((*reinterpret_cast< QString(*)>(_a[1]))); break; + case 12: _t->clickLine((*reinterpret_cast< QPoint(*)>(_a[1])),(*reinterpret_cast< float(*)>(_a[2])),(*reinterpret_cast< float(*)>(_a[3])),(*reinterpret_cast< float(*)>(_a[4])),(*reinterpret_cast< float(*)>(_a[5])),(*reinterpret_cast< float(*)>(_a[6])),(*reinterpret_cast< float(*)>(_a[7]))); break; + case 13: _t->nextRealGrid(); break; + case 14: _t->nextVec3Grid(); break; + case 15: _t->nextMesh(); break; + case 16: _t->nextParts(); break; + case 17: _t->nextPdata(); break; + case 18: _t->nextVec3Display(); break; + case 19: _t->nextPartDisplay(); break; + case 20: _t->nextMeshDisplay(); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::MainWnd::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::MainWnd::staticMetaObject = { + { &QMainWindow::staticMetaObject, qt_meta_stringdata_Manta__MainWnd, + qt_meta_data_Manta__MainWnd, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::MainWnd::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::MainWnd::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::MainWnd::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__MainWnd)) + return static_cast<void*>(const_cast< MainWnd*>(this)); + return QMainWindow::qt_metacast(_clname); +} + +int Manta::MainWnd::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QMainWindow::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 21) + qt_static_metacall(this, _c, _id, _a); + _id -= 21; + } + return _id; +} + +// SIGNAL 0 +void Manta::MainWnd::painterEvent(int _t1, int _t2) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} + +// SIGNAL 2 +void Manta::MainWnd::wakeMain() +{ + QMetaObject::activate(this, &staticMetaObject, 2, 0); +} + +// SIGNAL 3 +void Manta::MainWnd::setBackgroundMesh(Mesh * _t1) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 3, _a); +} + +// SIGNAL 4 +void Manta::MainWnd::killMain() +{ + QMetaObject::activate(this, &staticMetaObject, 4, 0); +} + +// SIGNAL 5 +void Manta::MainWnd::exitApp() +{ + QMetaObject::activate(this, &staticMetaObject, 5, 0); +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_mainwindow.cxx_parameters b/source/blender/python/manta_pp/gui/moc_mainwindow.cxx_parameters new file mode 100644 index 00000000000..0399d8bd0fb --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_mainwindow.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_mainwindow.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/mainwindow.h diff --git a/source/blender/python/manta_pp/gui/moc_meshpainter.cxx b/source/blender/python/manta_pp/gui/moc_meshpainter.cxx new file mode 100644 index 00000000000..a128a0fd7a2 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_meshpainter.cxx @@ -0,0 +1,93 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'meshpainter.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "meshpainter.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'meshpainter.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__MeshPainter[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 24, 20, 19, 19, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__MeshPainter[] = { + "Manta::MeshPainter\0\0bgr\0" + "setBackgroundMesh(Mesh*)\0" +}; + +void Manta::MeshPainter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + MeshPainter *_t = static_cast<MeshPainter *>(_o); + switch (_id) { + case 0: _t->setBackgroundMesh((*reinterpret_cast< Mesh*(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::MeshPainter::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::MeshPainter::staticMetaObject = { + { &LockedObjPainter::staticMetaObject, qt_meta_stringdata_Manta__MeshPainter, + qt_meta_data_Manta__MeshPainter, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::MeshPainter::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::MeshPainter::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::MeshPainter::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__MeshPainter)) + return static_cast<void*>(const_cast< MeshPainter*>(this)); + return LockedObjPainter::qt_metacast(_clname); +} + +int Manta::MeshPainter::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = LockedObjPainter::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_meshpainter.cxx_parameters b/source/blender/python/manta_pp/gui/moc_meshpainter.cxx_parameters new file mode 100644 index 00000000000..eb7319e1fab --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_meshpainter.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_meshpainter.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/meshpainter.h diff --git a/source/blender/python/manta_pp/gui/moc_painter.cxx b/source/blender/python/manta_pp/gui/moc_painter.cxx new file mode 100644 index 00000000000..7e2dc158f81 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_painter.cxx @@ -0,0 +1,170 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'painter.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "painter.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'painter.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__Painter[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 4, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 1, // signalCount + + // signals: signature, parameters, type, tag, flags + 25, 16, 15, 15, 0x05, + + // slots: signature, parameters, type, tag, flags + 44, 15, 15, 15, 0x0a, + 60, 52, 15, 15, 0x0a, + 79, 77, 15, 15, 0x2a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__Painter[] = { + "Manta::Painter\0\0gridsize\0setViewport(Vec3i)\0" + "paint()\0e,param\0doEvent(int,int)\0e\0" + "doEvent(int)\0" +}; + +void Manta::Painter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + Painter *_t = static_cast<Painter *>(_o); + switch (_id) { + case 0: _t->setViewport((*reinterpret_cast< const Vec3i(*)>(_a[1]))); break; + case 1: _t->paint(); break; + case 2: _t->doEvent((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 3: _t->doEvent((*reinterpret_cast< int(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::Painter::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::Painter::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_Manta__Painter, + qt_meta_data_Manta__Painter, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::Painter::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::Painter::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::Painter::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__Painter)) + return static_cast<void*>(const_cast< Painter*>(this)); + return QObject::qt_metacast(_clname); +} + +int Manta::Painter::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 4) + qt_static_metacall(this, _c, _id, _a); + _id -= 4; + } + return _id; +} + +// SIGNAL 0 +void Manta::Painter::setViewport(const Vec3i & _t1) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} +static const uint qt_meta_data_Manta__LockedObjPainter[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__LockedObjPainter[] = { + "Manta::LockedObjPainter\0" +}; + +void Manta::LockedObjPainter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObjectExtraData Manta::LockedObjPainter::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::LockedObjPainter::staticMetaObject = { + { &Painter::staticMetaObject, qt_meta_stringdata_Manta__LockedObjPainter, + qt_meta_data_Manta__LockedObjPainter, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::LockedObjPainter::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::LockedObjPainter::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::LockedObjPainter::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__LockedObjPainter)) + return static_cast<void*>(const_cast< LockedObjPainter*>(this)); + return Painter::qt_metacast(_clname); +} + +int Manta::LockedObjPainter::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = Painter::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_painter.cxx_parameters b/source/blender/python/manta_pp/gui/moc_painter.cxx_parameters new file mode 100644 index 00000000000..2dde75589ec --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_painter.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_painter.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/painter.h diff --git a/source/blender/python/manta_pp/gui/moc_particlepainter.cxx b/source/blender/python/manta_pp/gui/moc_particlepainter.cxx new file mode 100644 index 00000000000..a2ea9bacf4b --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_particlepainter.cxx @@ -0,0 +1,80 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'particlepainter.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "particlepainter.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'particlepainter.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__ParticlePainter[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__ParticlePainter[] = { + "Manta::ParticlePainter\0" +}; + +void Manta::ParticlePainter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObjectExtraData Manta::ParticlePainter::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::ParticlePainter::staticMetaObject = { + { &LockedObjPainter::staticMetaObject, qt_meta_stringdata_Manta__ParticlePainter, + qt_meta_data_Manta__ParticlePainter, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::ParticlePainter::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::ParticlePainter::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::ParticlePainter::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__ParticlePainter)) + return static_cast<void*>(const_cast< ParticlePainter*>(this)); + return LockedObjPainter::qt_metacast(_clname); +} + +int Manta::ParticlePainter::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = LockedObjPainter::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_particlepainter.cxx_parameters b/source/blender/python/manta_pp/gui/moc_particlepainter.cxx_parameters new file mode 100644 index 00000000000..efa42b9903c --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_particlepainter.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_particlepainter.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/particlepainter.h diff --git a/source/blender/python/manta_pp/gui/moc_qtmain.cxx b/source/blender/python/manta_pp/gui/moc_qtmain.cxx new file mode 100644 index 00000000000..7ead8306ef5 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_qtmain.cxx @@ -0,0 +1,182 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'qtmain.h' +** +** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "qtmain.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qtmain.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_Manta__GuiThread[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 20, 18, 17, 17, 0x0a, + 35, 17, 17, 17, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__GuiThread[] = { + "Manta::GuiThread\0\0e\0sendEvent(int)\0" + "exitApp()\0" +}; + +void Manta::GuiThread::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + GuiThread *_t = static_cast<GuiThread *>(_o); + switch (_id) { + case 0: _t->sendEvent((*reinterpret_cast< int(*)>(_a[1]))); break; + case 1: _t->exitApp(); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::GuiThread::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::GuiThread::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_Manta__GuiThread, + qt_meta_data_Manta__GuiThread, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::GuiThread::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::GuiThread::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::GuiThread::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__GuiThread)) + return static_cast<void*>(const_cast< GuiThread*>(this)); + return QObject::qt_metacast(_clname); +} + +int Manta::GuiThread::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 2) + qt_static_metacall(this, _c, _id, _a); + _id -= 2; + } + return _id; +} +static const uint qt_meta_data_Manta__MainThread[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 3, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 1, // signalCount + + // signals: signature, parameters, type, tag, flags + 25, 19, 18, 18, 0x05, + + // slots: signature, parameters, type, tag, flags + 40, 18, 18, 18, 0x0a, + 49, 18, 18, 18, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Manta__MainThread[] = { + "Manta::MainThread\0\0event\0sendToGui(int)\0" + "wakeUp()\0killMe()\0" +}; + +void Manta::MainThread::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + MainThread *_t = static_cast<MainThread *>(_o); + switch (_id) { + case 0: _t->sendToGui((*reinterpret_cast< int(*)>(_a[1]))); break; + case 1: _t->wakeUp(); break; + case 2: _t->killMe(); break; + default: ; + } + } +} + +const QMetaObjectExtraData Manta::MainThread::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject Manta::MainThread::staticMetaObject = { + { &QThread::staticMetaObject, qt_meta_stringdata_Manta__MainThread, + qt_meta_data_Manta__MainThread, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Manta::MainThread::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Manta::MainThread::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Manta::MainThread::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Manta__MainThread)) + return static_cast<void*>(const_cast< MainThread*>(this)); + return QThread::qt_metacast(_clname); +} + +int Manta::MainThread::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QThread::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 3) + qt_static_metacall(this, _c, _id, _a); + _id -= 3; + } + return _id; +} + +// SIGNAL 0 +void Manta::MainThread::sendToGui(int _t1) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} +QT_END_MOC_NAMESPACE diff --git a/source/blender/python/manta_pp/gui/moc_qtmain.cxx_parameters b/source/blender/python/manta_pp/gui/moc_qtmain.cxx_parameters new file mode 100644 index 00000000000..50315ba96f5 --- /dev/null +++ b/source/blender/python/manta_pp/gui/moc_qtmain.cxx_parameters @@ -0,0 +1,5 @@ +-DDEBUG +-DGUI=1 +-o +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/moc_qtmain.cxx +/Users/pr110/Documents/WorkingOnNow/gsoc/manta_git/xcode_build/pp/source/gui/qtmain.h diff --git a/source/blender/python/manta_pp/gui/painter.cpp b/source/blender/python/manta_pp/gui/painter.cpp new file mode 100644 index 00000000000..cc51c6bce20 --- /dev/null +++ b/source/blender/python/manta_pp/gui/painter.cpp @@ -0,0 +1,555 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for objects painting into the GL widget + * + ******************************************************************************/ + +#include "painter.h" +#include <QtOpenGL> +#include <sstream> +#include <iomanip> + +using namespace std; + +namespace Manta { + +//****************************************************************************** +// Base class + +void LockedObjPainter::doEvent(int e, int param) { + // try to obtain valid handle + if (!mObject) + nextObject(); + + // filter update events + if (e == UpdateFull) { + // always update + if (mObject) { + mObject->lock(); + update(); + mObject->unlock(); + mRequestUpdate = false; + } + } else if (e == UpdateRequest) { + // update if resource is available, otherwise wait until next step + mRequestUpdate = true; + if (mObject) { + if (mObject->tryLock()) { + update(); + mRequestUpdate = false; + mObject->unlock(); + } + } + } else if (e == UpdateStep) { + // update if requested only + if (mRequestUpdate) { + if (mObject) { + mObject->lock(); + update(); + mObject->unlock(); + mRequestUpdate = false; + } + } + } else { + // pass on all other events + processKeyEvent((PainterEvent)e, param); + } +} + +void LockedObjPainter::nextObject() { + if (PbClass::getNumInstances() == 0) return; + + int oldIndex = mObjIndex; + for(;;) { + mObjIndex = (mObjIndex + 1) % PbClass::getNumInstances(); + if (oldIndex == mObjIndex) break; + + PbClass* obj = PbClass::getInstance(mObjIndex); + if (obj->canConvertTo(getID()) && !obj->isHidden()) { + mObject = obj; + doEvent(UpdateRequest); + return; + } + if (oldIndex < 0) oldIndex = 0; // prevent endless loop on first run + } +} + +//****************************************************************************** +// Grid painter + +template<class T> +GridPainter<T>::GridPainter(FlagGrid** flags, QWidget* par) + : LockedObjPainter(par), mMaxVal(0), mDim(0), mPlane(0), mMax(0), mLocalGrid(NULL), + mFlags(flags), mInfo(NULL), mHide(false), mHideLocal(false), mDispMode(VecDispCentered), mValScale() +{ + mDim = 2; // Z plane + mPlane = 0; + mInfo = new QLabel(); + +} + +template<class T> +GridPainter<T>::~GridPainter() { + if (mLocalGrid) + delete mLocalGrid; +} + +template<class T> +void GridPainter<T>::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +template<class T> +void GridPainter<T>::update() { + Grid<T>* src = (Grid<T>*) mObject; + + if (!mLocalGrid) { + mLocalGrid = new Grid<T>(src->getParent()); + // int grid is base for resolution + if (src->getType() & GridBase::TypeInt) + emit setViewport(src->getSize()); + } + // reallocate if dimensions changed + if (mLocalGrid->getSize() != src->getSize()) { + delete mLocalGrid; + mLocalGrid = new Grid<T>(src->getParent()); + // int grid is base for resolution + if (src->getType() & GridBase::TypeInt) + emit setViewport(src->getSize()); + } + + mLocalGrid->copyFrom( *src ); // copy grid data and type marker + mLocalGrid->setName(src->getName()); + mLocalGrid->setParent(src->getParent()); + mMaxVal = mLocalGrid->getMaxAbsValue(); + + mPlane = clamp(mPlane, 0, mLocalGrid->getSize()[mDim]-1); + + updateText(); +} + +template<> string GridPainter<int>::getID() { return "Grid<int>"; } +template<> string GridPainter<Vec3>::getID() { return "Grid<Vec3>"; } +template<> string GridPainter<Real>::getID() { return "Grid<Real>"; } + +template<class T> +void GridPainter<T>::processKeyEvent(PainterEvent e, int param) +{ + if (e == EventSetDim) { + mDim = param; + if (mLocalGrid->is2D()) mDim = 2; + } else if (e == EventSetMax) { + mMax = param; + } else if (e == EventSetPlane) { + mPlane = param; + if (mObject) { + if (mMax>0) + mPlane = mPlane * mLocalGrid->getSize()[mDim] / mMax; + mPlane = clamp(mPlane, 0, mLocalGrid->getSize()[mDim]-1); + } + } else if (e == EventToggleGridDisplay) + mHide = !mHide; + else + processSpecificKeyEvent(e, param); + + updateText(); +} + +// get scale value for current grid from map, or create new +template<class T> +Real GridPainter<T>::getScale() { + if (!mObject) return 0; + + if (mValScale.find(mObject) == mValScale.end()) { + // init new scale value + Real s = 1.0; + if (mLocalGrid->getType() & GridBase::TypeVec3) + s = 0.4; + else if (mLocalGrid->getType() & GridBase::TypeLevelset) + s = 1.0; + mValScale[mObject] = s; + } + return mValScale[mObject]; + +} + +//****************************************************************************** +// Grid painter class specializations + +template<> +void GridPainter<int>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextInt) + nextObject(); +} + +template<> +void GridPainter<Real>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextReal) + nextObject(); + else if (e == EventScaleRealDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScaleRealUp && mObject) + mValScale[mObject] = getScale() * 2.0; + else if (e == EventNextRealDisplayMode) { + mDispMode = (mDispMode+1)%NumRealDispModes; + mHideLocal = (mDispMode==RealDispOff); + } +} + +template<> +void GridPainter<Vec3>::processSpecificKeyEvent(PainterEvent e, int param) { + if (e == EventNextVec) + nextObject(); + else if (e == EventScaleVecDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScaleVecUp && mObject) + mValScale[mObject] = getScale() * 2.0; + else if (e == EventNextVecDisplayMode) { + mDispMode = (mDispMode+1)%NumVecDispModes; + mHideLocal = (mDispMode==VecDispOff); + } +} + +template<> void GridPainter<int>::updateText() { + stringstream s; + if (mObject && (!mHide)) { + s << "Int Grid '" << mLocalGrid->getName() << "'" << endl; + } + mInfo->setText(s.str().c_str()); +} + +template<> void GridPainter<Real>::updateText() { + stringstream s; + + s << "Display Plane " << mPlane << " [" << (char)('X' + mDim) << "]" << endl << endl; + if (mObject) { + s << "Solver '" << mLocalGrid->getParent()->getName() << "'" << endl; + s << "Grid resolution [" << mLocalGrid->getSizeX() << ", " << mLocalGrid->getSizeY() << ", " << mLocalGrid->getSizeZ() << "]" << endl; + s << endl; + } + + if (mObject && !mHide && !mHideLocal) { + s << "Real Grid '" << mLocalGrid->getName() << "'" << endl; + s << "-> Max " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + mInfo->setText(s.str().c_str()); +} + +template<> void GridPainter<Vec3>::updateText() { + stringstream s; + if (mObject && !mHide && !mHideLocal) { + s << "Vec Grid '" << mLocalGrid->getName() << "'" << endl; + s << "-> Max norm " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + mInfo->setText(s.str().c_str()); +} + +// compute line intersection with the display plane +Vec3i getQuad(const Vec3& l0, const Vec3& l1, int dim, int plane, Real dx) { + Vec3 n(0.); n[dim] = 1; + Vec3 p0 = n*(plane+0.5); + Vec3 e = (l1-l0)/dx; + Vec3 e0 = l0/dx; + Real dotP = dot(p0-e0,n); + Real dotE = dot(e,n); + if (dotE == 0) + return Vec3i(-1,-1,-1); + Vec3 s = e0 + (dotP/dotE)*e; + return toVec3i(s); +} + +template<> string GridPainter<int>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << "Grid [ " << s.x << ", " << s.y << ", " << s.z << " ]" << endl << mLocalGrid->getName() << ": " << mLocalGrid->get(s) << endl; + return m.str(); +} + +template<> string GridPainter<Real>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << mLocalGrid->getName() << ": " << setprecision(2) << mLocalGrid->get(s) << endl; + return m.str(); +} + +template<> string GridPainter<Vec3>::clickLine(const Vec3& p0, const Vec3& p1) { + if (!mObject) return ""; + Vec3i s = getQuad(p0,p1,mDim,mPlane,mLocalGrid->getDx()); + if (!mLocalGrid->isInBounds(s)) return ""; + stringstream m; + m << mLocalGrid->getName() << ": [ " << setprecision(2) << mLocalGrid->get(s).x << ", " << + mLocalGrid->get(s).y << ", " << mLocalGrid->get(s).z << " ]" << endl; + return m.str(); +} + + +//****************************************************************************** +// Actual painting functions + +// GL helper functions + +// Macro to iterate through one plane +#define FOR_P_SLICE(__g,__dim,__plane) \ + for(Vec3i __g0(__fRange(Vec3i(0,0,0),__dim,__plane)), __g1(__fRange((__g)->getSize(),__dim,__plane+1)), p(__g0); p.z<__g1.z; p.z++) \ + for(p.y=__g0.y; p.y < __g1.y; p.y++) \ + for(p.x=__g0.x; p.x < __g1.x; p.x++) +inline Vec3i __fRange(Vec3i size, int dim, int plane) { Vec3i p(size); p[dim]=plane; return p; } + +// coordinate system : +// cell center(i,j,k) -> (i+0.5,j+0.5,k+0.5) / N +// + +void getCellCoordinates(const Vec3i& pos, Vec3 box[4], int dim) { + int dim2=(dim+1)%3; + Vec3 p0(pos.x, pos.y, pos.z); + Vec3 p1(pos.x+1, pos.y+1, pos.z+1); + p1[dim] = p0[dim] = pos[dim] + 0.5; + box[0] = p0; + box[3] = p0; box[3][dim2] = p1[dim2]; + box[1] = p1; box[1][dim2] = p0[dim2]; + box[2] = p1; +} +static inline void glVertex(const Vec3& v, const float dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} +void glBox(const Vec3& p0, const Vec3& p1, const float dx) { + const int box[24] = {0,1,0,2,0,4,7,6,7,5,7,3,1,3,1,5,2,3,2,6,4,5,4,6}; + for (int i=0;i<24;i++) { + const int b = box[i]; + glVertex(Vec3( (b&1) ? p1.x : p0.x, (b&2) ? p1.y : p0.y, (b&4) ? p1.z : p0.z), dx); + } +} + +// Paint gridlines +template<> void GridPainter<int>::paint() { + if (!mObject || mHide || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim]) + return; + float dx = mLocalGrid->getDx(); + Vec3 box[4]; + glColor3f(0.5,0,0); + + bool rbox = true; + bool skipFluid = mLocalGrid->getSize().max() > 40; + bool drawLines = mLocalGrid->getSize().max() <= 80; + if (drawLines) { + //glDepthFunc(GL_LESS); + glBegin(GL_LINES); + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + + int flag = 0; + flag = mLocalGrid->get(p); + + if (flag & FlagGrid::TypeObstacle) { + glColor3f(0.2,0.2,0.2); + } else if (flag & FlagGrid::TypeOutflow) { + glColor3f(0.9,0.2,0); + } else if (flag & FlagGrid::TypeEmpty) { + glColor3f(0.25,0,0); + } else if (flag & FlagGrid::TypeFluid) { + if(skipFluid) continue; + glColor3f(0,0,0.75); + } else { + glColor3f(0.5,0,0); // unknown + } + + getCellCoordinates(p, box, mDim); + for (int n=1;n<=8;n++) + glVertex(box[(n/2)%4], dx); + } + glEnd(); + //glDepthFunc(GL_ALWAYS); + } + + if (rbox) { + Vec3 p0(0.0), p1(toVec3(mLocalGrid->getSize())),p(p0); + glDepthFunc(GL_LESS); + glBegin(GL_LINES); + glBox(p0,p1,dx); + glEnd(); + glDepthFunc(GL_ALWAYS); + } +} + +// Paint box colors +template<> void GridPainter<Real>::paint() { + if (!mObject || mHide || mHideLocal || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim] || !mFlags || !(*mFlags)) + return; + + float dx = mLocalGrid->getDx(); + Vec3 box[4]; + glBegin(GL_QUADS); + Real scale = getScale(); + bool isLevelset = mLocalGrid->getType() & GridBase::TypeLevelset; + //glPolygonOffset(1.0,1.0); + //glDepthFunc(GL_LESS); + + const bool useOldDrawStyle = false; + if(useOldDrawStyle) { + // original mantaflow drawing style + FlagGrid *flags = *mFlags; + if (flags->getSize() != mLocalGrid->getSize()) flags = 0; + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + int flag = FlagGrid::TypeFluid; + if (flags && (mLocalGrid->getType() & GridBase::TypeLevelset) == 0) flag = flags->get(p); + if (flag & FlagGrid::TypeObstacle) + glColor3f(0.15,0.15,0.15); + else if (flag & FlagGrid::TypeOutflow) + glColor3f(0.3,0.0,0.0); + else if (flag & FlagGrid::TypeEmpty) + glColor3f(0.,0.2,0.); + else { + Real v = mLocalGrid->get(p) * scale; + + if (isLevelset) { + v = max(min(v*0.2, 1.0),-1.0); + if (v>=0) + glColor3f(v,0,0.5); + else + glColor3f(0.5, 1.0+v, 0.); + } else { + if (v>0) + glColor3f(v,0,0); + else + glColor3f(0,0,-v); + } + } + + if ((flag & FlagGrid::TypeEmpty) == 0) { + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + } + + } else { + // "new" drawing style + // ignore flags, its a bit dangerous to skip outside info + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) + { + Real v = mLocalGrid->get(p) * scale; + if (isLevelset) { + v = max(min(v*0.2, 1.0),-1.0); + if (v>=0) + glColor3f(v,0,0.5); + else + glColor3f(0.5, 1.0+v, 0.); + } else { + if (v>0) + glColor3f(v,v,v); + else + glColor3f(-v,0,0); + } + + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + } + + glEnd(); + //glDepthFunc(GL_ALWAYS); + //glPolygonOffset(0,0); +} + +// Paint velocity vectors +template<> void GridPainter<Vec3>::paint() { + if (!mObject || mHide || mHideLocal || mPlane <0 || mPlane >= mLocalGrid->getSize()[mDim]) + return; + + float dx = mLocalGrid->getDx(); + bool mac = mLocalGrid->getType() & GridBase::TypeMAC; + const Real scale = getScale(); + + if( (mDispMode==VecDispCentered) || (mDispMode==VecDispStaggered) ) { + + // regular velocity drawing mode + glBegin(GL_LINES); + + FOR_P_SLICE(mLocalGrid, mDim, mPlane) { + Vec3 vel = mLocalGrid->get(p) * scale; + Vec3 pos (p.x+0.5, p.y+0.5, p.z+0.5); + if (mDispMode==VecDispCentered) { + if (mac) { + if (p.x < mLocalGrid->getSizeX()-1) + vel.x = 0.5 * (vel.x + scale * mLocalGrid->get(p.x+1,p.y,p.z).x); + if (p.y < mLocalGrid->getSizeY()-1) + vel.y = 0.5 * (vel.y + scale * mLocalGrid->get(p.x,p.y+1,p.z).y); + if (p.z < mLocalGrid->getSizeZ()-1) + vel.z = 0.5 * (vel.z + scale * mLocalGrid->get(p.x,p.y,p.z+1).z); + } + glColor3f(0,1,0); + glVertex(pos, dx); + glColor3f(1,1,0); + glVertex(pos+vel*1.2, dx); + } else if (mDispMode==VecDispStaggered) { + for (int d=0; d<3; d++) { + if (fabs(vel[d]) < 1e-2) continue; + Vec3 p1(pos); + if (mac) + p1[d] -= 0.5f; + Vec3 color(0.0); + color[d] = 1; + glColor3f(color.x, color.y, color.z); + glVertex(p1, dx); + glColor3f(1,1,0); + p1[d] += vel[d]; + glVertex(p1, dx); + } + } + } + glEnd(); + + } else if (mDispMode==VecDispUv) { + // draw as uv coordinates , note - this will completely hide the real grid display! + Vec3 box[4]; + glBegin(GL_QUADS); + FOR_P_SLICE(mLocalGrid, mDim, mPlane) + { + Vec3 v = mLocalGrid->get(p) * scale; + for(int c=0; c<3; ++c) { + if(v[c]<0.) v[c] *= -1.; + v[c] = fmod( (Real)v[c], (Real)1.); + } + //v *= mLocalGrid->get(0)[0]; // debug, show uv grid weight as brightness of values + glColor3f(v[0],v[1],v[2]); + getCellCoordinates(p, box, mDim); + for (int n=0;n<4;n++) + glVertex(box[n], dx); + } + glEnd(); + } +} + + +// explicit instantiation +template class GridPainter<int>; +template class GridPainter<Real>; +template class GridPainter<Vec3>; + +} // namespace + + diff --git a/source/blender/python/manta_pp/gui/painter.h b/source/blender/python/manta_pp/gui/painter.h new file mode 100644 index 00000000000..c2e8af192f0 --- /dev/null +++ b/source/blender/python/manta_pp/gui/painter.h @@ -0,0 +1,127 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for objects painting into the GL widget + * + ******************************************************************************/ + +#ifndef _PAINTER_H_ +#define _PAINTER_H_ + +#include <QWidget> +#include <QLabel> +#include <map> +#include "grid.h" + +namespace Manta { + +// forward decl. +class PbClass; + +//! Base class for all painter +/*! Derived classes have to implement paint, doEvent */ +class Painter : public QObject { + Q_OBJECT +public: + enum PainterEvent { + EventNone = 0, UpdateRequest, UpdateFull, UpdateStep, + EventScaleVecUp, EventScaleVecDown, EventNextRealDisplayMode, EventScaleRealUp, EventScaleRealDown, EventChangePlane, + EventSetPlane, EventSetDim, EventNextInt, EventNextReal, EventNextVec, EventNextVecDisplayMode, + EventNextMesh, EventMeshMode, EventToggleGridDisplay, EventScaleMeshUp, EventScaleMeshDown, EventMeshColorMode, + EventNextSystem, EventToggleParticles, EventNextParticleDisplayMode, EventToggleBackgroundMesh, EventSetMax, + EventScalePdataDown, EventScalePdataUp }; + + enum RealDisplayModes { RealDispOff=0, RealDispStd, NumRealDispModes }; + + enum VecDisplayModes { VecDispOff=0, VecDispCentered, VecDispStaggered, VecDispUv, NumVecDispModes }; + + Painter(QWidget* par = 0) : QObject(par) {} + virtual ~Painter() {} + + virtual std::string clickLine(const Vec3& p0, const Vec3& p1) { return ""; } + virtual void attachWidget(QLayout* layout) {} +signals: + void setViewport(const Vec3i& gridsize); + +public slots: + virtual void paint() = 0; + virtual void doEvent(int e, int param=0) = 0; +}; + +//! Base clas for all painters that require access to a locked PbClass +/*! Derived classes have to implement paint, update, getID, processKeyEvent. doEvent is handled in this class */ +class LockedObjPainter : public Painter { + Q_OBJECT +public: + LockedObjPainter(QWidget* par = 0) : Painter(par), mRequestUpdate(false), mObject(NULL), mObjIndex(-1) {} + + void doEvent(int e, int param=0); // don't overload, use processKeyEvent and update instead + +protected: + void nextObject(); + virtual std::string getID() = 0; + virtual void update() = 0; + virtual void processKeyEvent(PainterEvent e, int param) = 0; + + bool mRequestUpdate; + PbClass* mObject; + int mObjIndex; +}; + +//! Painter object for int,Real,Vec3 grids +template<class T> +class GridPainter : public LockedObjPainter { +public: + GridPainter(FlagGrid** flags = NULL, QWidget* par = 0); + ~GridPainter(); + + void paint(); + void attachWidget(QLayout* layout); + Grid<T>** getGridPtr() { return &mLocalGrid; } + int getPlane() { return mPlane; } + int getDim() { return mDim; } + int getMax() { return mMax; } + virtual std::string clickLine(const Vec3& p0, const Vec3& p1); + +protected: + std::string getID(); + Real getScale(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + void processSpecificKeyEvent(PainterEvent e, int param); + //void paintGridLines(bool lines, bool box); + + Real mMaxVal; + int mDim, mPlane, mMax; + Grid<T>* mLocalGrid; + FlagGrid** mFlags; + QLabel* mInfo; + bool mHide; // hide all grids? + bool mHideLocal; // hide only this type? + int mDispMode; // display modes + std::map<PbClass*, Real> mValScale; +}; + +} + +#endif + + diff --git a/source/blender/python/manta_pp/gui/painter.h.reg b/source/blender/python/manta_pp/gui/painter.h.reg new file mode 100644 index 00000000000..73dfb7feeee --- /dev/null +++ b/source/blender/python/manta_pp/gui/painter.h.reg @@ -0,0 +1 @@ +#include "gui/painter.h" diff --git a/source/blender/python/manta_pp/gui/painter.h.reg.cpp b/source/blender/python/manta_pp/gui/painter.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/painter.h.reg.cpp diff --git a/source/blender/python/manta_pp/gui/particlepainter.cpp b/source/blender/python/manta_pp/gui/particlepainter.cpp new file mode 100644 index 00000000000..fca56377f85 --- /dev/null +++ b/source/blender/python/manta_pp/gui/particlepainter.cpp @@ -0,0 +1,370 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting particle systems + * + ******************************************************************************/ + +#include <ctime> +#include "particlepainter.h" +#include <sstream> +#include <iomanip> +#include <QtOpenGL> +#include "vortexpart.h" +#include "turbulencepart.h" + +using namespace std; + +namespace Manta { + +ParticlePainter::ParticlePainter(GridPainter<int>* gridRef, QWidget* par) + : LockedObjPainter(par), mGridRef(gridRef), mLocal(0), mMode(PaintVel), mDisplayMode(0), + mLastPdata(-1), mHavePdata(false), mMaxVal(0.) +{ + mInfo = new QLabel(); +} + +ParticlePainter::~ParticlePainter() { + if (mLocal) + delete mLocal; +} + +void ParticlePainter::attachWidget(QLayout* layout) { + layout->addWidget(mInfo); +} + +void ParticlePainter::update() { + ParticleBase* src = (ParticleBase*) mObject; + + // always reallocate + if (mLocal) + delete mLocal; + + mLocal = src->clone(); + + updateText(); +} + +string ParticlePainter::getID() { return "ParticleBase"; } + +Real ParticlePainter::getScale() { + if (!mObject) return 0; + + if (mValScale.find(mObject) == mValScale.end()) { + Real s = 1.0; + //if (mLocalGrid->getType() & GridBase::TypeVec3) s = 0.4; + mValScale[mObject] = s; + } + return mValScale[mObject]; + +} + +void ParticlePainter::processKeyEvent(PainterEvent e, int param) { + if (e == EventNextSystem) + nextObject(); + else if (e == EventScalePdataDown && mObject) + mValScale[mObject] = getScale() * 0.5; + else if (e == EventScalePdataUp && mObject) + mValScale[mObject] = getScale() * 2.0; + else if (e == EventToggleParticles) { + mMode++; // apply modulo later depending on particle system + //if(mMode>PaintVel) mMode=PaintOff; + } + else if (e == EventNextParticleDisplayMode) { + mDisplayMode++; + } + else return; + + updateText(); +} + +void ParticlePainter::updateText() { + ostringstream s; + + if (mObject && !(mMode==PaintOff) ) { + s << mLocal->infoString() << endl; + s << mPdataInfo; + if(mHavePdata) { + s << "-> Max " << fixed << setprecision(2) << mMaxVal << " Scale " << getScale() << endl; + } + } + mInfo->setText( s.str().c_str() ); +} + + +static inline void glVertex(const Vec3& v, Real dx) { + glVertex3f(v.x * dx, v.y * dx, v.z * dx); +} + +static inline void glColor(const Vec3& color) { + glColor3f( std::max(0.0f,std::min(1.0f,(float)color.x)), + std::max(0.0f,std::min(1.0f,(float)color.y)), + std::max(0.0f,std::min(1.0f,(float)color.z)) ); +} + +void ParticlePainter::paint() { + if (!mObject) return; + if (mMode == PaintOff) return; + float dx = mLocal->getParent()->getDx(); + mHavePdata = false; + mMaxVal = 0.; + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); // disable depth test for particles, clashes with display plane for regular ones + glDisable(GL_LIGHTING); + + // draw points + if(mLocal->getType() == ParticleBase::VORTEX) { + VortexParticleSystem* vp = (VortexParticleSystem*) mLocal; + glColor3f(1,1,0); + for(int i=0; i<vp->size(); i++) { + if (vp->isActive(i)) { + Vec3 pos = (*vp)[i].pos; + + glPointSize((*vp)[i].sigma); + + glBegin(GL_POINTS); + glVertex(pos, dx); + glEnd(); + } + } + } else if (mLocal->getType() == ParticleBase::FILAMENT) { + // Filaments don't work yet + /*VortexFilamentSystem* fp = (VortexFilamentSystem*) mLocal; + glColor3f(1,1,0); + + for(int i=0; i<fp->segSize(); i++) { + if (!fp->isSegActive(i)) continue; + const VortexRing& r = fp->seg(i); + + glPointSize(1.0); + glBegin(GL_LINES); + for(int j=0; j<r.size(); j++) { + glVertex( (*fp)[r.idx0(j)].pos, dx); + glVertex( (*fp)[r.idx1(j)].pos, dx); + } + glEnd(); + } */ + } else if(mLocal->getType() == ParticleBase::TURBULENCE) { + TurbulenceParticleSystem* vp = (TurbulenceParticleSystem*) mLocal; + glPointSize(2.5); + glColor3f(0,1,0); + glBegin(GL_POINTS); + for(int i=0; i<(int)vp->size(); i++) { + Vec3 pos = (*vp)[i].pos; + glColor((*vp)[i].color); + glVertex(pos, dx); + + } + glEnd(); + + } else if(mLocal->getType() == ParticleBase::PARTICLE) { + paintBasicSys(); + } + + glPointSize(1.0); + glEnable(GL_DEPTH_TEST); +} + +void ParticlePainter::paintBasicSys() { + BasicParticleSystem* bp = (BasicParticleSystem*) mLocal; + //int dim = mGridRef->getDim(); + + // obtain current plane & draw settings + int dim = mGridRef->getDim(); + Real factor = mGridRef->getMax() / mLocal->getParent()->getGridSize()[dim]; + int plane = factor * mGridRef->getPlane(); + Real scale = getScale(); + float dx = mLocal->getParent()->getDx(); + + // draw other particle data, if available + int pdataId = mMode % (bp->getNumPdata() + 2); + std::ostringstream infoStr; + bool drewPoints = false; + + if( pdataId==0 ) { + // dont draw any points + infoStr << "Off\n"; + drewPoints = true; + } else if( pdataId==1 ) { + // dont draw data, only flags with center below + infoStr << "Drawing center & flags\n"; + } else if (bp->getNumPdata() > 0) { + int pdNum = pdataId-2; // start at 0 + ParticleDataBase* pdb = bp->getPdata(pdNum); + + switch (pdb->getType() ) { + + case ParticleDataBase::TypeReal: { + ParticleDataImpl<Real>* pdi = dynamic_cast<ParticleDataImpl<Real>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( pdi->get(i), mMaxVal ); + Real val = pdi->get(i) * scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", real\n"; + } break; + + case ParticleDataBase::TypeInt: { + ParticleDataImpl<int>* pdi = dynamic_cast<ParticleDataImpl<int>*>(pdb); + if(!pdi) break; + mHavePdata = true; + drewPoints = true; + glPointSize(1.5); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + Real val = pdi->get(i); + mMaxVal = std::max( val, mMaxVal ); + val *= scale; + glColor3f(0,val,0); + glVertex(pos, dx); + } + glEnd(); + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", int\n"; + } break; + + case ParticleDataBase::TypeVec3: { + ParticleDataImpl<Vec3>* pdi = dynamic_cast<ParticleDataImpl<Vec3>*>(pdb); + if(!pdi) break; + mHavePdata = true; + + // particle vector data can be drawn in different ways... + mDisplayMode = mDisplayMode%3; + + switch(mDisplayMode) { + case 0: // lines + glBegin(GL_LINES); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = pdi->get(i) * scale; + glColor3f(0.5,0.0,0); + glVertex(pos, dx); + pos += val; + glColor3f(0.5,1.0,0); + glVertex(pos, dx); + } + glEnd(); + break; + case 1: + // colored points + glPointSize(2.0); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = pdi->get(i) * scale; + for(int c=0; c<3; ++c) val[c] = fmod( (Real)val[c], (Real)1.); + + glColor3f(val[0],val[1],val[2]); + glVertex(pos, dx); + //pos += val; + //glColor3f(0.5,1.0,0); + //glVertex(pos, dx); + } + glEnd(); + drewPoints = true; + break; + case 2: + glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + // colored by magnitude all + glPointSize(2.0); + glBegin(GL_POINTS); + for(int i=0; i<(int)bp->size(); i++) { + if (!bp->isActive(i)) continue; + Vec3 pos = (*bp)[i].pos; + mMaxVal = std::max( norm(pdi->get(i)), mMaxVal ); + Vec3 val = Vec3( norm( pdi->get(i) * scale ) ); + val[2] += 0.5; // base blue + for(int c=0; c<3; ++c) val[c] = std::min( (Real)val[c], (Real)1.); + + glColor3f(val[0],val[1],val[2]); + glVertex(pos, dx); + } + glEnd(); + drewPoints = true; + break; + } + + infoStr << "Pdata '"<<pdi->getName()<<"' #"<<pdNum<<", vec3\n"; + } break; + + default: { + // skip... + } break; + } + } + + mPdataInfo = infoStr.str(); + // enforce refresh upon change + if(mLastPdata!=pdataId) { + mLastPdata = pdataId; + updateText(); + } + + // otherwise draw center + if(!drewPoints) { + glPointSize(1.5); + glBegin(GL_POINTS); + + for(int i=0; i<(int)bp->size(); i++) { + Vec3 pos = (*bp)[i].pos; + if (pos[dim] < plane || pos[dim] > plane + 1.0f) continue; + + if(!bp->isActive(i) ) { + glColor3f(1.0, 0., 0.); // deleted, red + } else if(bp->getStatus(i) & ParticleBase::PNEW ) { + glColor3f(0.0, 1.0, 0.); // new, green + } else { + //glColor3f(0, 0.0, 1.0); // regular, blue + glColor3f(1.0, 1.0, 1.0); // regular, white - hi contrast + } + glVertex(pos, dx); + + } + glEnd(); + } + + // draw basic part sys done +} + +} // namespace + + + diff --git a/source/blender/python/manta_pp/gui/particlepainter.h b/source/blender/python/manta_pp/gui/particlepainter.h new file mode 100644 index 00000000000..ba482b38bc6 --- /dev/null +++ b/source/blender/python/manta_pp/gui/particlepainter.h @@ -0,0 +1,74 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Painting particle systems + * + ******************************************************************************/ + +#ifndef _PARTICLEPAINTER_H_ +#define _PARTICLEPAINTER_H_ + +#include "painter.h" +#include "particle.h" + +namespace Manta { + +//! Painter object for Particle Systems +class ParticlePainter : public LockedObjPainter { + Q_OBJECT +public: + ParticlePainter(GridPainter<int>* gridRef, QWidget* par = 0); + ~ParticlePainter(); + + void paint(); + void attachWidget(QLayout* layout); + + enum PaintModes { PaintOff=0, PaintVel=1, PaintPos=2 }; + +protected: + std::string getID(); + Real getScale(); + void update(); + void updateText(); + void processKeyEvent(PainterEvent e, int param); + + GridPainter<int>* mGridRef; + ParticleBase* mLocal; + QLabel* mInfo; + + //! for standard particle systems, this is used to select the data channel (display is changed with displayMode below) + int mMode; + //! for BasicParticleSystem , change the way data channels are displayed + int mDisplayMode; + + int mLastPdata; + bool mHavePdata; + Real mMaxVal; + std::string mPdataInfo; + std::map<PbClass*, Real> mValScale; + + void paintBasicSys(); +}; + +} // namespace + +#endif + + diff --git a/source/blender/python/manta_pp/gui/particlepainter.h.reg b/source/blender/python/manta_pp/gui/particlepainter.h.reg new file mode 100644 index 00000000000..b1580c96d67 --- /dev/null +++ b/source/blender/python/manta_pp/gui/particlepainter.h.reg @@ -0,0 +1 @@ +#include "gui/particlepainter.h" diff --git a/source/blender/python/manta_pp/gui/particlepainter.h.reg.cpp b/source/blender/python/manta_pp/gui/particlepainter.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/particlepainter.h.reg.cpp diff --git a/source/blender/python/manta_pp/gui/qtmain.cpp b/source/blender/python/manta_pp/gui/qtmain.cpp new file mode 100644 index 00000000000..6c46d607cb2 --- /dev/null +++ b/source/blender/python/manta_pp/gui/qtmain.cpp @@ -0,0 +1,163 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT threads + * + ******************************************************************************/ + +#include "mainwindow.h" +#include "qtmain.h" +#include "customctrl.h" + +using namespace std; + +// execute python script +// from pymain.cpp +extern void runScript(vector<string>& args); + +namespace Manta { + +GuiThread* gGuiThread = NULL; +MainThread* gMainThread = NULL; + +MainThread::MainThread(vector<string>& args) : mFinished(false), mArgs(args) { +} + +void MainThread::run() { + runScript(mArgs); +} + +void MainThread::sendAndWait(int e) { + mMutex.lock(); + emit sendToGui(e); + while(!mWait.wait(&mMutex, 250)) + if (gGuiThread->getWindow()->closeRequest()) { + mMutex.unlock(); + throw Error("User interrupt"); + } + mMutex.unlock(); +} + +void MainThread::send(int e) { + emit sendToGui(e); +} + +void MainThread::killMe() { + if (!mFinished) { + wait(1000); + if (!mFinished) { + cout << "worker thread still running, terminate" << endl; + terminate(); + return; + } + } + wait(); +} + +void MainThread::wakeUp() { + mMutex.lock(); + mWait.wakeAll(); + mMutex.unlock(); +} + +GuiThread::GuiThread(QApplication& app) : mApp(app), mWnd() { +} + +void GuiThread::sendEvent(int e) { + mApp.postEvent(&mWnd, new QEvent((QEvent::Type)e)); +} + +void GuiThread::exitApp() { + mApp.exit(1); +} + +void guiMain(int argc, char* argv[]) { + QApplication app(argc, argv); + + // parse arguments + vector<string> args; + for (int i=1;i<argc;i++) args.push_back(argv[i]); + + // Show file dialog if no argument is present + if (argc <= 1) { + QString filename = QFileDialog::getOpenFileName(0, "Open scene file", "", "Python scene files (*.py)"); + args.push_back(filename.toLatin1().data()); + } + + GuiThread gui(app); + MainThread worker(args); + + gGuiThread = &gui; + gMainThread = &worker; + + // connect thread wakeup and termination signals + QObject::connect(&worker, SIGNAL(sendToGui(int)), &gui, SLOT(sendEvent(int))); + QObject::connect(gui.getWindow(), SIGNAL(wakeMain()), &worker, SLOT(wakeUp())); + QObject::connect(gui.getWindow(), SIGNAL(killMain()), &worker, SLOT(killMe())); + QObject::connect(gui.getWindow(), SIGNAL(exitApp()), &gui, SLOT(exitApp())); + app.setQuitOnLastWindowClosed(true); + + // Start main program threads + worker.start(); + app.exec(); +} + +void guiWaitFinish() { + gMainThread->setFinished(); + gMainThread->send((int)MainWnd::EventInstantKill); + /* + if (gGuiThread->getWindow()->closeRequest()) + return; + + gMainThread->sendAndWait((int)MainWnd::EventFinalUpdate); + gGuiThread->getWindow()->pause(); + while (gGuiThread->getWindow()->pauseRequest()) + gMainThread->threadSleep(10); */ +} + +//****************************************************************************** +// Python adapter class + + +// external callback functions +void updateQtGui(bool full, int frame, const string& curPlugin) { + if (!gGuiThread->getWindow()->isVisible()) return; + if (gGuiThread->getWindow()->closeRequest()) throw Error("User interrupt"); + + if (full && frame >= 0) gGuiThread->getWindow()->setStep(frame); + gMainThread->sendAndWait(full ? (int)MainWnd::EventFullUpdate : (int)MainWnd::EventStepUpdate); + + if (gGuiThread->getWindow()->pauseRequest()) { + if (!curPlugin.empty()) { + cout << "Step: " << curPlugin << endl; + } + gGuiThread->getWindow()->setPauseStatus(true); + while (gGuiThread->getWindow()->pauseRequest()) { + gMainThread->threadSleep(10); + } + if (gGuiThread->getWindow()->closeRequest()) throw Error("User interrupt"); + gGuiThread->getWindow()->setPauseStatus(false); + } + gGuiThread->getWindow()->stepReset(full); +} + +} //namespace + + diff --git a/source/blender/python/manta_pp/gui/qtmain.h b/source/blender/python/manta_pp/gui/qtmain.h new file mode 100644 index 00000000000..289e817159c --- /dev/null +++ b/source/blender/python/manta_pp/gui/qtmain.h @@ -0,0 +1,89 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * QT threads + * + ******************************************************************************/ +#ifndef _QTMAIN_H_ +#define _QTMAIN_H_ + +#include <QThread> +#include <QApplication> +#include <vector> +#include <QMutex> +#include <QWaitCondition> +#include "mainwindow.h" +#include "manta.h" + +namespace Manta { + +//! encapsulates GUI thread +class GuiThread : public QObject { + Q_OBJECT +public: + + GuiThread(QApplication& app); + + //! obtain window handle + inline MainWnd* getWindow() { return &mWnd; } + +public slots: + void sendEvent(int e); + void exitApp(); + +protected: + QApplication& mApp; + MainWnd mWnd; +}; + +//! encapsulates working/python thread +class MainThread : public QThread { + Q_OBJECT +public: + MainThread(std::vector<std::string>& args); + + //! send event to GUI and wait for completion + void sendAndWait(int e); + void send(int e); + + //! sleep for given number of milliseconds + inline void threadSleep(int msec) { msleep(msec); } + inline bool isFinished() { return mFinished; } + inline void setFinished() { mFinished = true; } + +public slots: + void wakeUp(); + void killMe(); + +signals: + void sendToGui(int event); + +protected: + QMutex mMutex; + QWaitCondition mWait; + bool mFinished; + std::vector<std::string> mArgs; + void run(); +}; + +} // namespace + +#endif + diff --git a/source/blender/python/manta_pp/gui/qtmain.h.reg b/source/blender/python/manta_pp/gui/qtmain.h.reg new file mode 100644 index 00000000000..0d932b9aeba --- /dev/null +++ b/source/blender/python/manta_pp/gui/qtmain.h.reg @@ -0,0 +1 @@ +#include "gui/qtmain.h" diff --git a/source/blender/python/manta_pp/gui/qtmain.h.reg.cpp b/source/blender/python/manta_pp/gui/qtmain.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/gui/qtmain.h.reg.cpp diff --git a/source/blender/python/manta_pp/hginfo.h b/source/blender/python/manta_pp/hginfo.h new file mode 100644 index 00000000000..4caf264131c --- /dev/null +++ b/source/blender/python/manta_pp/hginfo.h @@ -0,0 +1,4 @@ + + +#define MANTA_HG_VERSION "commit 79cf1d3f26350f1a10c829b1367e8e5d096979dd" + diff --git a/source/blender/python/manta_pp/kernel.cpp b/source/blender/python/manta_pp/kernel.cpp new file mode 100644 index 00000000000..746ad2b77db --- /dev/null +++ b/source/blender/python/manta_pp/kernel.cpp @@ -0,0 +1,49 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Function and macros for defining compution kernels over grids + * + ******************************************************************************/ + +#include "kernel.h" +#include "grid.h" +#include "particle.h" + +namespace Manta { + +KernelBase::KernelBase(const GridBase* base, int bnd) : + maxX (base->getSizeX()-bnd), + maxY (base->getSizeY()-bnd), + maxZ (base->is3D() ? (base->getSizeZ()-bnd) : 1), + minZ (base->is3D() ? bnd : 0), + X (base->getStrideX()), + Y (base->getStrideY()), + Z (base->getStrideZ()), + size (base->getSizeX() * base->getSizeY() * base->getSizeZ()), + threadId(0),threadNum(1) {} + +KernelBase::KernelBase(int sz) : + size(sz), + threadId(0),threadNum(1) {} + + +} // namespace + + diff --git a/source/blender/python/manta_pp/kernel.h b/source/blender/python/manta_pp/kernel.h new file mode 100644 index 00000000000..143fca906c7 --- /dev/null +++ b/source/blender/python/manta_pp/kernel.h @@ -0,0 +1,95 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Function and macros for defining compution kernels over grids + * + ******************************************************************************/ + +#ifndef _KERNEL_H +#define _KERNEL_H + +#ifdef TBB +# include <tbb/blocked_range3d.h> +# include <tbb/blocked_range.h> +# include <tbb/parallel_for.h> +# include <tbb/parallel_reduce.h> +#endif + +#ifdef OPENMP +# include <omp.h> +#endif + +namespace Manta { +// fwd decl +class GridBase; +class ParticleBase; + + +// simple iteration +#define FOR_IJK_BND(grid, bnd) \ + for(int k=((grid).is3D() ? bnd : 0),__kmax=((grid).is3D() ? ((grid).getSizeZ()-bnd) : 1); k<__kmax; k++) \ + for(int j=bnd; j<(grid).getSizeY()-bnd; j++) \ + for(int i=bnd; i<(grid).getSizeX()-bnd; i++) + +#define FOR_IJK_REVERSE(grid) \ + for(int k=(grid).getSizeZ()-1; k>=0; k--) \ + for(int j=(grid).getSizeY()-1; j>=0; j--) \ + for(int i=(grid).getSizeX()-1; i>=0; i--) + +#define FOR_IDX(grid) \ + for(int idx=0, total=(grid).getSizeX()*(grid).getSizeY()*(grid).getSizeZ(); idx<total; idx++) + +#define FOR_IJK(grid) FOR_IJK_BND(grid, 0) + +#define FOR_PARTS(parts) \ + for(int idx=0, total=(parts).size(); idx<total; idx++) + +struct KernelBase { + int maxX, maxY, maxZ, minZ; + int X, Y, Z; + int size; + //! store thread info for this kernel + int threadId, threadNum; + + KernelBase(int num); + KernelBase(const GridBase* base, int bnd); + //KernelBase(int _maxX, int _maxY, int _maxZ, int _maxC, int _minZ, int _X, int _Y, int _Z); + + // specify in your derived classes: + + // kernel operators + // ijk mode: void operator() (size_t idx) + // idx mode: void operator() (size_t i, size_t j, size_t k) + + // reduce mode: + // void join(classname& other) + // void setup() +}; + +} // namespace + +// Define plugin documentation group +// all kernels will automatically be added to this group +/*! @defgroup Kernels Computation Kernels + */ + +#endif + + diff --git a/source/blender/python/manta_pp/kernel.h.reg b/source/blender/python/manta_pp/kernel.h.reg new file mode 100644 index 00000000000..6457157c184 --- /dev/null +++ b/source/blender/python/manta_pp/kernel.h.reg @@ -0,0 +1 @@ +#include "kernel.h" diff --git a/source/blender/python/manta_pp/kernel.h.reg.cpp b/source/blender/python/manta_pp/kernel.h.reg.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/python/manta_pp/kernel.h.reg.cpp diff --git a/source/blender/python/manta_pp/levelset.cpp b/source/blender/python/manta_pp/levelset.cpp new file mode 100644 index 00000000000..be1c2ffdcfe --- /dev/null +++ b/source/blender/python/manta_pp/levelset.cpp @@ -0,0 +1,346 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Levelset + * + ******************************************************************************/ + +#include "levelset.h" +#include "fastmarch.h" +#include "kernel.h" +#include "mcubes.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +//************************************************************************ +// Helper functions and kernels for marching + +static const int FlagInited = FastMarch<FmHeapEntryOut, +1>::FlagInited; + +// neighbor lookup vectors +static const Vec3i neighbors[6] = { Vec3i(-1,0,0), Vec3i(1,0,0), Vec3i(0,-1,0), Vec3i(0,1,0), Vec3i(0,0,-1), Vec3i(0,0,1) }; + + + struct InitFmIn : public KernelBase { InitFmIn(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType) : KernelBase(&flags,1) ,flags(flags),fmFlags(fmFlags),phi(phi),ignoreWalls(ignoreWalls),obstacleType(obstacleType) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType ) { + const int idx = flags.index(i,j,k); + const Real v = phi[idx]; + if (v>=0 && (!ignoreWalls || (flags[idx] & obstacleType) == 0)) + fmFlags[idx] = FlagInited; + else + fmFlags[idx] = 0; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<int>& getArg1() { return fmFlags; } typedef Grid<int> type1;inline LevelsetGrid& getArg2() { return phi; } typedef LevelsetGrid type2;inline bool& getArg3() { return ignoreWalls; } typedef bool type3;inline int& getArg4() { return obstacleType; } typedef int type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,fmFlags,phi,ignoreWalls,obstacleType); } FlagGrid& flags; Grid<int>& fmFlags; LevelsetGrid& phi; bool ignoreWalls; int obstacleType; }; + + + struct InitFmOut : public KernelBase { InitFmOut(FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType) : KernelBase(&flags,1) ,flags(flags),fmFlags(fmFlags),phi(phi),ignoreWalls(ignoreWalls),obstacleType(obstacleType) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<int>& fmFlags, LevelsetGrid& phi, bool ignoreWalls, int obstacleType ) { + const int idx = flags.index(i,j,k); + const Real v = phi[idx]; + if (ignoreWalls) { + fmFlags[idx] = (v<0) ? FlagInited : 0; + if ((flags[idx] & obstacleType) != 0) { + fmFlags[idx] = 0; + phi[idx] = 0; + } + } + else + fmFlags[idx] = (v<0) ? FlagInited : 0; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<int>& getArg1() { return fmFlags; } typedef Grid<int> type1;inline LevelsetGrid& getArg2() { return phi; } typedef LevelsetGrid type2;inline bool& getArg3() { return ignoreWalls; } typedef bool type3;inline int& getArg4() { return obstacleType; } typedef int type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,fmFlags,phi,ignoreWalls,obstacleType); } FlagGrid& flags; Grid<int>& fmFlags; LevelsetGrid& phi; bool ignoreWalls; int obstacleType; }; + + + struct SetUninitialized : public KernelBase { SetUninitialized(Grid<int>& fmFlags, LevelsetGrid& phi, const Real val) : KernelBase(&fmFlags,1) ,fmFlags(fmFlags),phi(phi),val(val) { run(); } inline void op(int i, int j, int k, Grid<int>& fmFlags, LevelsetGrid& phi, const Real val ) { + if (fmFlags(i,j,k) != FlagInited) + phi(i,j,k) = val; +} inline Grid<int>& getArg0() { return fmFlags; } typedef Grid<int> type0;inline LevelsetGrid& getArg1() { return phi; } typedef LevelsetGrid type1;inline const Real& getArg2() { return val; } typedef Real type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, fmFlags,phi,val); } Grid<int>& fmFlags; LevelsetGrid& phi; const Real val; }; + +template<bool inward> +inline bool isAtInterface(Grid<int>& fmFlags, LevelsetGrid& phi, const Vec3i& p) { + // check for interface + for (int nb=0; nb<6; nb++) { + const Vec3i pn(p + neighbors[nb]); + if (!fmFlags.isInBounds(pn)) continue; + + if (fmFlags(pn) != FlagInited) continue; + if ((inward && phi(pn) >= 0) || + (!inward && phi(pn) < 0)) return true; + } + return false; +} + +// helper function to compute normal +inline Vec3 getNormal(const Grid<Real>& data, int i, int j, int k) { + if (i > data.getSizeX()-2) i= data.getSizeX()-2; + if (j > data.getSizeY()-2) j= data.getSizeY()-2; + if (k > data.getSizeZ()-2) k= data.getSizeZ()-2; + if (i < 1) i = 1; + if (j < 1) j = 1; + if (k < 1) k = 1; + return Vec3( data(i+1,j ,k ) - data(i-1,j ,k ) , + data(i ,j+1,k ) - data(i ,j-1,k ) , + data(i ,j ,k+1) - data(i ,j ,k-1) ); +} + +//************************************************************************ +// Levelset class def + +LevelsetGrid::LevelsetGrid(FluidSolver* parent, bool show) + : Grid<Real>(parent, show) +{ + mType = (GridType)(TypeLevelset | TypeReal); +} + +Real LevelsetGrid::invalidTimeValue() { + return FastMarch<FmHeapEntryOut, 1>::InvalidTime(); +} + +//! Kernel: perform levelset union + struct KnJoin : public KernelBase { KnJoin(Grid<Real>& a, const Grid<Real>& b) : KernelBase(&a,0) ,a(a),b(b) { run(); } inline void op(int idx, Grid<Real>& a, const Grid<Real>& b ) { + a[idx] = min(a[idx], b[idx]); +} inline Grid<Real>& getArg0() { return a; } typedef Grid<Real> type0;inline const Grid<Real>& getArg1() { return b; } typedef Grid<Real> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, a,b); } Grid<Real>& a; const Grid<Real>& b; }; + +void LevelsetGrid::join(const LevelsetGrid& o) { + KnJoin(*this, o); +} + +//! re-init levelset and extrapolate velocities (in & out) +// note - uses flags to identify border (could also be done based on ls values) +void LevelsetGrid::reinitMarching( + FlagGrid& flags, Real maxTime, MACGrid* velTransport, + bool ignoreWalls, bool correctOuterLayer, int obstacleType + , Grid<Real>* normSpeed ) +{ + const int dim = (is3D() ? 3 : 2); + + Grid<int> fmFlags(mParent); + LevelsetGrid& phi = *this; + + FastMarch<FmHeapEntryIn, -1> marchIn (flags, fmFlags, phi, maxTime, NULL, NULL); + + // march inside + InitFmIn (flags, fmFlags, phi, ignoreWalls, obstacleType); + + FOR_IJK_BND(flags, 1) { + if (fmFlags(i,j,k) == FlagInited) continue; + if ((flags(i,j,k) & obstacleType) != 0) continue; + const Vec3i p(i,j,k); + + if(isAtInterface<true>(fmFlags, phi, p)) { + // set value + fmFlags(p) = FlagInited; + + // add neighbors that are not at the interface + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + if ((flags.get(pn) & obstacleType) != 0) continue; + + // check neighbors of neighbor + if (phi(pn) < 0 && !isAtInterface<true>(fmFlags, phi, pn)) { + marchIn.addToList(pn, p); + } + } + } + } + marchIn.performMarching(); + // done with inwards marching + + // now march out... + + // set un initialized regions + SetUninitialized (fmFlags, phi, -maxTime - 1.); + + InitFmOut (flags, fmFlags, phi, ignoreWalls, obstacleType); + + FastMarch<FmHeapEntryOut, +1> marchOut(flags, fmFlags, phi, maxTime, velTransport, normSpeed); + + // NT_DEBUG + if(normSpeed && velTransport) { + FOR_IJK_BND(flags, 1) { + Vec3 vel = velTransport->getCentered(i,j,k); + Vec3 norm = getNormal(phi, i,j,k); normalize(norm); + (*normSpeed)(i,j,k) = dot( norm , vel ); + } + } + + // by default, correctOuterLayer is on + if (correctOuterLayer) { + // normal version, inwards march is done, now add all outside values (0..2] to list + // note, this might move the interface a bit! but keeps a nice signed distance field... + FOR_IJK_BND(flags, 1) { + if ((flags(i,j,k) & obstacleType) != 0) continue; + const Vec3i p(i,j,k); + + // check nbs + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + + if (fmFlags(pn) != FlagInited) continue; + if ((flags.get(pn) & obstacleType) != 0) continue; + + const Real nbPhi = phi(pn); + + // only add nodes near interface, not e.g. outer boundary vs. invalid region + if (nbPhi < 0 && nbPhi >= -2) + marchOut.addToList(p, pn); + } + } + } else { + // alternative version, keep interface, do not distort outer cells + // add all ouside values, but not those at the IF layer + FOR_IJK_BND(flags, 1) { + if ((flags(i,j,k) & obstacleType) != 0) continue; + + // only look at ouside values + const Vec3i p(i,j,k); + if (phi(p) < 0) continue; + + if (isAtInterface<false>(fmFlags, phi, p)) { + // now add all non, interface neighbors + fmFlags(p) = FlagInited; + + // add neighbors that are not at the interface + for (int nb=0; nb<2*dim; nb++) { + const Vec3i pn(p + neighbors[nb]); // index always valid due to bnd=1 + if ((flags.get(pn) & obstacleType) != 0) continue; + + // check neighbors of neighbor + if (phi(pn) > 0 && !isAtInterface<false>(fmFlags, phi, pn)) + marchOut.addToList(pn, p); + } + } + } + } + marchOut.performMarching(); + + // set un initialized regions + SetUninitialized (fmFlags, phi, +maxTime + 1.); +} + +void LevelsetGrid::initFromFlags(FlagGrid& flags, bool ignoreWalls) { + FOR_IDX(*this) { + if (flags.isFluid(idx) || (ignoreWalls && flags.isObstacle(idx))) + mData[idx] = -0.5; + else + mData[idx] = 0.5; + } +} + +// note - the following functions are experimental, might be removed at some point NT_DEBUG + struct knGridRemapLsMask : public KernelBase { knGridRemapLsMask(Grid<Real>& me, Real min, Real max, Real fac) : KernelBase(&me,0) ,me(me),min(min),max(max),fac(fac) { run(); } inline void op(int idx, Grid<Real>& me, Real min, Real max, Real fac ) { + me[idx] = (clamp(me[idx], min, max) - min) * fac; + + // now we have 0..1 range, convert to hat around 1/2 + me[idx] = me[idx] * 2.; + if(me[idx]>1.0) me[idx] = 2. - me[idx]; +} inline Grid<Real>& getArg0() { return me; } typedef Grid<Real> type0;inline Real& getArg1() { return min; } typedef Real type1;inline Real& getArg2() { return max; } typedef Real type2;inline Real& getArg3() { return fac; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,min,max,fac); } Grid<Real>& me; Real min; Real max; Real fac; }; +// remap min/max range to hat function around (min+max)/2 +void remapLsMask(Grid<Real>& phi, Real min, Real max) { + Real fac = 0.; + if ( fabs(max-min) > VECTOR_EPSILON ) fac = 1. / (max-min); + knGridRemapLsMask(phi, min, max, fac); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "remapLsMask" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& phi = *_args.getPtr<Grid<Real> >("phi",0,&_lock); Real min = _args.get<Real >("min",1,&_lock); Real max = _args.get<Real >("max",2,&_lock); _retval = getPyNone(); remapLsMask(phi,min,max); _args.check(); } pbFinalizePlugin(parent,"remapLsMask" ); return _retval; } catch(std::exception& e) { pbSetError("remapLsMask",e.what()); return 0; } } static const Pb::Register _RP_remapLsMask ("","remapLsMask",_W_0); + +//! run marching cubes to create a mesh for the 0-levelset +void LevelsetGrid::createMesh(Mesh& mesh) { + assertMsg(is3D(), "Only 3D grids supported so far"); + + mesh.clear(); + + const Real invalidTime = invalidTimeValue(); + const Real isoValue = 1e-4; + + // create some temp grids + Grid<int> edgeVX(mParent); + Grid<int> edgeVY(mParent); + Grid<int> edgeVZ(mParent); + + for(int i=0; i<mSize.x-1; i++) + for(int j=0; j<mSize.y-1; j++) + for(int k=0; k<mSize.z-1; k++) { + Real value[8] = { get(i,j,k), get(i+1,j,k), get(i+1,j+1,k), get(i,j+1,k), + get(i,j,k+1), get(i+1,j,k+1), get(i+1,j+1,k+1), get(i,j+1,k+1) }; + + // build lookup index, check for invalid times + bool skip = false; + int cubeIdx = 0; + for (int l=0;l<8;l++) { + value[l] *= -1; + if (-value[l] <= invalidTime) + skip = true; + if (value[l] < isoValue) + cubeIdx |= 1<<l; + } + if (skip || (mcEdgeTable[cubeIdx] == 0)) continue; + + // where to look up if this point already exists + int triIndices[12]; + int *eVert[12] = { &edgeVX(i,j,k), &edgeVY(i+1,j,k), &edgeVX(i,j+1,k), &edgeVY(i,j,k), + &edgeVX(i,j,k+1), &edgeVY(i+1,j,k+1), &edgeVX(i,j+1,k+1), &edgeVY(i,j,k+1), + &edgeVZ(i,j,k), &edgeVZ(i+1,j,k), &edgeVZ(i+1,j+1,k), &edgeVZ(i,j+1,k) }; + + const Vec3 pos[9] = { Vec3(i,j,k), Vec3(i+1,j,k), Vec3(i+1,j+1,k), Vec3(i,j+1,k), + Vec3(i,j,k+1), Vec3(i+1,j,k+1), Vec3(i+1,j+1,k+1), Vec3(i,j+1,k+1) }; + + for (int e=0; e<12; e++) { + if (mcEdgeTable[cubeIdx] & (1<<e)) { + // vertex already calculated ? + if (*eVert[e] == 0) { + // interpolate edge + const int e1 = mcEdges[e*2 ]; + const int e2 = mcEdges[e*2+1]; + const Vec3 p1 = pos[ e1 ]; // scalar field pos 1 + const Vec3 p2 = pos[ e2 ]; // scalar field pos 2 + const float valp1 = value[ e1 ]; // scalar field val 1 + const float valp2 = value[ e2 ]; // scalar field val 2 + const float mu = (isoValue - valp1) / (valp2 - valp1); + + // init isolevel vertex + Node vertex; + vertex.pos = p1 + (p2-p1)*mu; + vertex.normal = getNormalized( + getNormal( *this, i+cubieOffsetX[e1], j+cubieOffsetY[e1], k+cubieOffsetZ[e1]) * (1.0-mu) + + getNormal( *this, i+cubieOffsetX[e2], j+cubieOffsetY[e2], k+cubieOffsetZ[e2]) * ( mu)) ; + + triIndices[e] = mesh.addNode(vertex) + 1; + + // store vertex + *eVert[e] = triIndices[e]; + } else { + // retrieve from vert array + triIndices[e] = *eVert[e]; + } + } + } + + // Create the triangles... + for(int e=0; mcTriTable[cubeIdx][e]!=-1; e+=3) { + mesh.addTri( Triangle( triIndices[ mcTriTable[cubeIdx][e+0]] - 1, + triIndices[ mcTriTable[cubeIdx][e+1]] - 1, + triIndices[ mcTriTable[cubeIdx][e+2]] - 1)); + } + } + + //mesh.rebuildCorners(); + //mesh.rebuildLookup(); +} + + +} //namespace + + diff --git a/source/blender/python/manta_pp/levelset.h b/source/blender/python/manta_pp/levelset.h new file mode 100644 index 00000000000..6b1746ada23 --- /dev/null +++ b/source/blender/python/manta_pp/levelset.h @@ -0,0 +1,56 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Levelset + * + ******************************************************************************/ + +#ifndef _LEVELSET_H_ +#define _LEVELSET_H_ + +#include "grid.h" + +namespace Manta { +class Mesh; + +//! Special function for levelsets +class LevelsetGrid : public Grid<Real> {public: + LevelsetGrid(FluidSolver* parent, bool show = true); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "LevelsetGrid::LevelsetGrid" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); bool show = _args.getOpt<bool >("show",1,true,&_lock); obj = new LevelsetGrid(parent,show); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"LevelsetGrid::LevelsetGrid" ); return 0; } catch(std::exception& e) { pbSetError("LevelsetGrid::LevelsetGrid",e.what()); return -1; } } + + //! reconstruct the levelset using fast marching + + +void reinitMarching(FlagGrid& flags, Real maxTime=4.0, MACGrid* velTransport=NULL, bool ignoreWalls=false, bool correctOuterLayer=true, int obstacleType = FlagGrid::TypeObstacle, Grid<Real>* scalarTransport = NULL ); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); LevelsetGrid* pbo = dynamic_cast<LevelsetGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "LevelsetGrid::reinitMarching"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Real maxTime = _args.getOpt<Real >("maxTime",1,4.0,&_lock); MACGrid* velTransport = _args.getPtrOpt<MACGrid >("velTransport",2,NULL,&_lock); bool ignoreWalls = _args.getOpt<bool >("ignoreWalls",3,false,&_lock); bool correctOuterLayer = _args.getOpt<bool >("correctOuterLayer",4,true,&_lock); int obstacleType = _args.getOpt<int >("obstacleType",5,FlagGrid::TypeObstacle,&_lock); Grid<Real>* scalarTransport = _args.getPtrOpt<Grid<Real> >("scalarTransport",6,NULL ,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->reinitMarching(flags,maxTime,velTransport,ignoreWalls,correctOuterLayer,obstacleType,scalarTransport); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"LevelsetGrid::reinitMarching"); return _retval; } catch(std::exception& e) { pbSetError("LevelsetGrid::reinitMarching",e.what()); return 0; } } + //! create a triangle mesh from the levelset isosurface + void createMesh(Mesh& mesh); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); LevelsetGrid* pbo = dynamic_cast<LevelsetGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "LevelsetGrid::createMesh"); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->createMesh(mesh); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"LevelsetGrid::createMesh"); return _retval; } catch(std::exception& e) { pbSetError("LevelsetGrid::createMesh",e.what()); return 0; } } + + //! union with another levelset + void join(const LevelsetGrid& o); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); LevelsetGrid* pbo = dynamic_cast<LevelsetGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "LevelsetGrid::join"); PyObject *_retval = 0; { ArgLocker _lock; const LevelsetGrid& o = *_args.getPtr<LevelsetGrid >("o",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->join(o); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"LevelsetGrid::join"); return _retval; } catch(std::exception& e) { pbSetError("LevelsetGrid::join",e.what()); return 0; } } + + //! initialize levelset from flags (+/- 0.5 heaviside) + void initFromFlags(FlagGrid& flags, bool ignoreWalls=false); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); LevelsetGrid* pbo = dynamic_cast<LevelsetGrid*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "LevelsetGrid::initFromFlags"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); bool ignoreWalls = _args.getOpt<bool >("ignoreWalls",1,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->initFromFlags(flags,ignoreWalls); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"LevelsetGrid::initFromFlags"); return _retval; } catch(std::exception& e) { pbSetError("LevelsetGrid::initFromFlags",e.what()); return 0; } } + static Real invalidTimeValue(); public: PbArgs _args;} +#define _C_LevelsetGrid +; + +} //namespace +#endif + + diff --git a/source/blender/python/manta_pp/levelset.h.reg b/source/blender/python/manta_pp/levelset.h.reg new file mode 100644 index 00000000000..d8191c3dceb --- /dev/null +++ b/source/blender/python/manta_pp/levelset.h.reg @@ -0,0 +1,9 @@ +#include "levelset.h" ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","LevelsetGrid","Grid<$BT$>"); template<> const char* Namify<LevelsetGrid >::S = "LevelsetGrid"; +>LevelsetGrid^ +@LevelsetGrid^^Grid^Real ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","LevelsetGrid",LevelsetGrid::_W_0); ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","reinitMarching",LevelsetGrid::_W_1); ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","createMesh",LevelsetGrid::_W_2); ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","join",LevelsetGrid::_W_3); ++LevelsetGrid^ static const Pb::Register _R_$IDX$ ("LevelsetGrid","initFromFlags",LevelsetGrid::_W_4); diff --git a/source/blender/python/manta_pp/levelset.h.reg.cpp b/source/blender/python/manta_pp/levelset.h.reg.cpp new file mode 100644 index 00000000000..200a459e80b --- /dev/null +++ b/source/blender/python/manta_pp/levelset.h.reg.cpp @@ -0,0 +1,22 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "levelset.h" +namespace Manta { +#ifdef _C_LevelsetGrid + static const Pb::Register _R_0 ("LevelsetGrid","LevelsetGrid","Grid<Real>"); template<> const char* Namify<LevelsetGrid >::S = "LevelsetGrid"; + static const Pb::Register _R_1 ("LevelsetGrid","LevelsetGrid",LevelsetGrid::_W_0); + static const Pb::Register _R_2 ("LevelsetGrid","reinitMarching",LevelsetGrid::_W_1); + static const Pb::Register _R_3 ("LevelsetGrid","createMesh",LevelsetGrid::_W_2); + static const Pb::Register _R_4 ("LevelsetGrid","join",LevelsetGrid::_W_3); + static const Pb::Register _R_5 ("LevelsetGrid","initFromFlags",LevelsetGrid::_W_4); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/mesh.cpp b/source/blender/python/manta_pp/mesh.cpp new file mode 100644 index 00000000000..2c6322003b6 --- /dev/null +++ b/source/blender/python/manta_pp/mesh.cpp @@ -0,0 +1,800 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Meshes + * + * note: this is only a temporary solution, details are bound to change + * long term goal is integration with Split&Merge code by Wojtan et al. + * + ******************************************************************************/ + +#include "mesh.h" +#include "integrator.h" +#include "fileio.h" +#include "kernel.h" +#include "shapes.h" +#include <stack> + +using namespace std; +namespace Manta { + +Mesh::Mesh(FluidSolver* parent) : PbClass(parent) { +} + +Mesh::~Mesh() { +} + +Mesh* Mesh::clone() { + Mesh* nm = new Mesh(mParent); + *nm = *this; + nm->setName(getName()); + return nm; +} + +Real Mesh::computeCenterOfMass(Vec3& cm) const { + + // use double precision for summation, otherwise too much error accumulation + double vol=0; + Vector3D<double> cmd(0.0); + for(size_t tri=0; tri < mTris.size(); tri++) { + Vector3D<double> p1(toVec3d(getNode(tri,0))); + Vector3D<double> p2(toVec3d(getNode(tri,1))); + Vector3D<double> p3(toVec3d(getNode(tri,2))); + + double cvol = dot(cross(p1,p2),p3) / 6.0; + cmd += (p1+p2+p3) * (cvol/3.0); + vol += cvol; + } + if (vol != 0.0) cmd /= vol; + + cm = toVec3(cmd); + return (Real) vol; +} + +void Mesh::clear() { + mNodes.clear(); + mTris.clear(); + mCorners.clear(); + m1RingLookup.clear(); + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->resize(0); + for(size_t i=0; i<mTriChannels.size(); i++) + mTriChannels[i]->resize(0); +} + +Mesh& Mesh::operator=(const Mesh& o) { + // wipe current data + clear(); + if (mNodeChannels.size() != o.mNodeChannels.size() || + mTriChannels.size() != o.mTriChannels.size()) + errMsg("can't copy mesh, channels not identical"); + mNodeChannels.clear(); + mTriChannels.clear(); + + // copy corner, nodes, tris + mCorners = o.mCorners; + mNodes = o.mNodes; + mTris = o.mTris; + m1RingLookup = o.m1RingLookup; + + // copy channels + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i] = o.mNodeChannels[i]; + for(size_t i=0; i<o.mTriChannels.size(); i++) + mTriChannels[i] = o.mTriChannels[i]; + + return *this; +} + +void Mesh::load(string name, bool append) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".gz") // assume bobj gz + readBobjFile(name, this, append); + else if (ext == ".obj") + readObjFile(name, this, append); + else + errMsg("file '" + name +"' filetype not supported"); + + + // dont always rebuild... + //rebuildCorners(); + //rebuildLookup(); +} + +void Mesh::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".obj") + writeObjFile(name, this); + else if (ext == ".gz") + writeBobjFile(name, this); + else + errMsg("file '" + name +"' filetype not supported"); +} + +void Mesh::fromShape(Shape& shape, bool append) { + if (!append) + clear(); + shape.generateMesh(this); +} + +void Mesh::resizeTris(int numTris) { + mTris .resize(numTris ); + rebuildChannels(); +} +void Mesh::resizeNodes(int numNodes) { + mNodes.resize(numNodes); + rebuildChannels(); +} + +//! do a quick check whether a rebuild is necessary, and if yes do rebuild +void Mesh::rebuildQuickCheck() { + if(mCorners.size() != 3*mTris.size()) + rebuildCorners(); + if(m1RingLookup.size() != mNodes.size()) + rebuildLookup(); +} + +void Mesh::rebuildCorners(int from, int to) { + mCorners.resize(3*mTris.size()); + if (to < 0) to = mTris.size(); + + // fill in basic info + for (int tri=from; tri<to; tri++) { + for (int c=0; c<3; c++) { + const int idx = tri*3+c; + mCorners[idx].tri = tri; + mCorners[idx].node = mTris[tri].c[c]; + mCorners[idx].next = 3*tri+((c+1)%3); + mCorners[idx].prev = 3*tri+((c+2)%3); + mCorners[idx].opposite = -1; + } + } + + // set opposite info + int maxc = to*3; + for (int c=from*3; c<maxc; c++) { + int next = mCorners[mCorners[c].next].node; + int prev = mCorners[mCorners[c].prev].node; + + // find corner with same next/prev nodes + for (int c2=c+1; c2<maxc; c2++) { + int next2 = mCorners[mCorners[c2].next].node; + if (next2 != next && next2 != prev) continue; + int prev2 = mCorners[mCorners[c2].prev].node; + if (prev2 != next && prev2 != prev) continue; + + // found + mCorners[c].opposite = c2; + mCorners[c2].opposite = c; + break; + } + if (mCorners[c].opposite < 0) { + // didn't find opposite + errMsg("can't rebuild corners, index without an opposite"); + } + } + + rebuildChannels(); +} + +void Mesh::rebuildLookup(int from, int to) { + if (from==0 && to<0) m1RingLookup.clear(); + m1RingLookup.resize(mNodes.size()); + if (to<0) to = mTris.size(); + from *=3; to *= 3; + for (int i=from; i< to; i++) { + const int node = mCorners[i].node; + m1RingLookup[node].nodes.insert(mCorners[mCorners[i].next].node); + m1RingLookup[node].nodes.insert(mCorners[mCorners[i].prev].node); + m1RingLookup[node].tris.insert(mCorners[i].tri); + } +} + +void Mesh::rebuildChannels() { + for(size_t i=0; i<mTriChannels.size(); i++) + mTriChannels[i]->resize(mTris.size()); + for(size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->resize(mNodes.size()); +} + + + struct KnAdvectMeshInGrid : public KernelBase { KnAdvectMeshInGrid(vector<Node>& nodes, const FlagGrid& flags, const MACGrid& vel, const Real dt) : KernelBase(nodes.size()) ,nodes(nodes),flags(flags),vel(vel),dt(dt) ,u((size)) { run(); } inline void op(int idx, vector<Node>& nodes, const FlagGrid& flags, const MACGrid& vel, const Real dt ,vector<Vec3> & u) { + if (nodes[idx].flags & Mesh::NfFixed) + u[idx] = _0; + else if (!flags.isInBounds(nodes[idx].pos,1)) + u[idx] = _0; + else + u[idx] = vel.getInterpolated(nodes[idx].pos) * dt; +} inline operator vector<Vec3> () { return u; } inline vector<Vec3> & getRet() { return u; } inline vector<Node>& getArg0() { return nodes; } typedef vector<Node> type0;inline const FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline const MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline const Real& getArg3() { return dt; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, nodes,flags,vel,dt,u); } vector<Node>& nodes; const FlagGrid& flags; const MACGrid& vel; const Real dt; vector<Vec3> u; }; + +// advection plugin +void Mesh::advectInGrid(FlagGrid& flaggrid, MACGrid& vel, int integrationMode) { + KnAdvectMeshInGrid kernel(mNodes, flaggrid, vel, getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void Mesh::scale(Vec3 s) { + for (size_t i=0; i<mNodes.size(); i++) + mNodes[i].pos *= s; +} + +void Mesh::offset(Vec3 o) { + for (size_t i=0; i<mNodes.size(); i++) + mNodes[i].pos += o; +} + +void Mesh::removeTri(int tri) { + // delete triangles by overwriting them with elements from the end of the array. + if(tri!=(int)mTris.size()-1) { + // if this is the last element, and it is marked for deletion, + // don't waste cycles transfering data to itself, + // and DEFINITELY don't transfer .opposite data to other, untainted triangles. + + // old corners hold indices on the end of the corners array + // new corners holds indices in the new spot in the middle of the array + Corner* oldcorners[3]; + Corner* newcorners[3]; + int oldtri = mTris.size()-1; + for (int c=0; c<3; c++) { + oldcorners[c] = &corners(oldtri,c); + newcorners[c] = &corners(tri, c); + } + + // move the position of the triangle + mTris[tri] = mTris[oldtri]; + + // 1) update c.node, c.opposite (c.next and c.prev should be fine as they are) + for (int c=0; c<3; c++) { + newcorners[c]->node = mTris[tri].c[c]; + newcorners[c]->opposite = oldcorners[c]->opposite; + } + + // 2) c.opposite.opposite = c + for (int c=0; c<3; c++) { + if (newcorners[c]->opposite>=0) + mCorners[newcorners[c]->opposite].opposite = 3*tri+c; + } + + // update tri lookup + for (int c=0; c<3; c++) { + int node = mTris[tri].c[c]; + m1RingLookup[node].tris.erase(oldtri); + m1RingLookup[node].tris.insert(tri); + } + } + + // transfer tri props + for(size_t p=0; p < mTriChannels.size(); p++) + mTriChannels[p]->remove(tri); + + // pop the triangle and corners out of the vector + mTris.pop_back(); + mCorners.resize(mTris.size()*3); +} + +void Mesh::removeNodes(const vector<int>& deletedNodes) { + // After we delete the nodes that are marked for removal, + // the size of mNodes will be the current size - the size of the deleted array. + // We are going to move the elements at the end of the array + // (everything with an index >= newsize) + // to the deleted spots. + // We have to map all references to the last few nodes to their new locations. + int newsize = (int)(mNodes.size() - deletedNodes.size()); + + vector<int> new_index (deletedNodes.size()); + int di,ni; + for(ni=0; ni<(int)new_index.size(); ni++) + new_index[ni] = 0; + for(di=0; di<(int)deletedNodes.size(); di++) { + if(deletedNodes[di] >= newsize) + new_index[deletedNodes[di]-newsize] = -1; // tag this node as invalid + } + for(di=0,ni=0; ni<(int)new_index.size(); ni++,di++) { + // we need to find a valid node to move + // we marked invalid nodes in the earlier loop with a (-1), + // so pick anything but those + while(ni<(int)new_index.size() && new_index[ni]==-1) + ni++; + + if(ni>=(int)new_index.size()) + break; + + // next we need to find a valid spot to move the node to. + // we iterate through deleted[] until we find a valid spot + while(di<(int)new_index.size() && deletedNodes[di]>=newsize) + di++; + + // now we assign the valid node to the valid spot + new_index[ni] = deletedNodes[di]; + } + + // Now we have a map of valid indices. + // we move node[newsize+i] to location new_index[i]. + // We ignore the nodes with a -1 index, because they should not be moved. + for(int i=0; i<(int)new_index.size(); i++) { + if(new_index[i]!=-1) + mNodes[ new_index[i] ] = mNodes[ newsize+i ]; + } + mNodes.resize(newsize); + + // handle vertex properties + for (size_t i=0; i<mNodeChannels.size(); i++) + mNodeChannels[i]->renumber(new_index, newsize); + + // finally, we reconnect everything that used to point to this vertex. + for(size_t tri=0, n=0; tri<mTris.size(); tri++) { + for (int c=0; c<3; c++,n++) { + if (mCorners[n].node >= newsize) { + int newindex = new_index[mCorners[n].node - newsize]; + mCorners[n].node = newindex; + mTris[mCorners[n].tri].c[c] = newindex; + } + } + } + + // renumber 1-ring + for(int i=0; i<(int)new_index.size(); i++) { + if(new_index[i]!=-1) { + m1RingLookup[new_index[i]].nodes.swap(m1RingLookup[newsize+i].nodes); + m1RingLookup[new_index[i]].tris.swap(m1RingLookup[newsize+i].tris); + } + } + m1RingLookup.resize(newsize); + vector<int> reStack(new_index.size()); + for(int i=0; i<newsize; i++) { + set<int>& cs = m1RingLookup[i].nodes; + int reNum = 0; + // find all nodes > newsize + set<int>::reverse_iterator itend = cs.rend(); + for (set<int>::reverse_iterator it = cs.rbegin(); it != itend; ++it) { + if (*it < newsize) break; + reStack[reNum++] = *it; + } + // kill them and insert shifted values + if (reNum > 0) { + cs.erase(cs.find(reStack[reNum-1]), cs.end()); + for (int j=0; j<reNum; j++) { + cs.insert(new_index[reStack[j]-newsize]); +#ifdef DEBUG + if (new_index[reStack[j]-newsize] == -1) + errMsg("invalid node present in 1-ring set"); +#endif + } + } + } +} + +void Mesh::mergeNode(int node, int delnode) { + set<int>& ring = m1RingLookup[delnode].nodes; + for(set<int>::iterator it = ring.begin(); it != ring.end(); ++it) { + m1RingLookup[*it].nodes.erase(delnode); + if (*it != node) { + m1RingLookup[*it].nodes.insert(node); + m1RingLookup[node].nodes.insert(*it); + } + } + set<int>& ringt = m1RingLookup[delnode].tris; + for(set<int>::iterator it = ringt.begin(); it != ringt.end(); ++it) { + const int t = *it; + for (int c=0; c<3; c++) { + if (mCorners[3*t+c].node == delnode) { + mCorners[3*t+c].node = node; + mTris[t].c[c] = node; + } + } + m1RingLookup[node].tris.insert(t); + } + for(size_t i=0; i<mNodeChannels.size(); i++) { + // weight is fixed to 1/2 for now + mNodeChannels[i]->mergeWith(node, delnode, 0.5); + } +} + +void Mesh::removeTriFromLookup(int tri) { + for(int c=0; c<3; c++) { + int node = mTris[tri].c[c]; + m1RingLookup[node].tris.erase(tri); + } +} + +void Mesh::addCorner(Corner a) { + mCorners.push_back(a); +} + +int Mesh::addTri(Triangle a) { + mTris.push_back(a); + for (int c=0;c<3;c++) { + int node = a.c[c]; + int nextnode = a.c[(c+1)%3]; + if ((int)m1RingLookup.size() <= node) m1RingLookup.resize(node+1); + if ((int)m1RingLookup.size() <= nextnode) m1RingLookup.resize(nextnode+1); + m1RingLookup[node].nodes.insert(nextnode); + m1RingLookup[nextnode].nodes.insert(node); + m1RingLookup[node].tris.insert(mTris.size()-1); + } + return mTris.size()-1; +} + +int Mesh::addNode(Node a) { + mNodes.push_back(a); + if (m1RingLookup.size() < mNodes.size()) + m1RingLookup.resize(mNodes.size()); + return mNodes.size()-1; +} + +void Mesh::computeVertexNormals() { + for (size_t i=0; i<mNodes.size(); i++) { + mNodes[i].normal = 0.0; + } + for (size_t t=0; t<mTris.size(); t++) { + Vec3 p0 = getNode(t,0), p1 = getNode(t,1), p2 = getNode(t,2); + Vec3 n0 = p0-p1, n1 = p1-p2, n2 = p2-p0; + Real l0 = normSquare(n0), l1 = normSquare(n1), l2 = normSquare(n2); + + Vec3 nm = cross(n0,n1); + + mNodes[mTris[t].c[0]].normal += nm * (1.0 / (l0*l2)); + mNodes[mTris[t].c[1]].normal += nm * (1.0 / (l0*l1)); + mNodes[mTris[t].c[2]].normal += nm * (1.0 / (l1*l2)); + } + for (size_t i=0; i<mNodes.size(); i++) { + normalize(mNodes[i].normal); + } +} + +void Mesh::fastNodeLookupRebuild(int corner) { + int node = mCorners[corner].node; + m1RingLookup[node].nodes.clear(); + m1RingLookup[node].tris.clear(); + int start = mCorners[corner].prev; + int current = start; + do { + m1RingLookup[node].nodes.insert(mCorners[current].node); + m1RingLookup[node].tris.insert(mCorners[current].tri); + current = mCorners[mCorners[current].opposite].next; + if (current < 0) + errMsg("Can't use fastNodeLookupRebuild on incomplete surfaces"); + } while (current != start); +} + +void Mesh::sanityCheck(bool strict, vector<int>* deletedNodes, map<int,bool>* taintedTris) { + const int nodes = numNodes(), tris = numTris(), corners = 3*tris; + for(size_t i=0; i<mNodeChannels.size(); i++) { + if (mNodeChannels[i]->size() != nodes) + errMsg("Node channel size mismatch"); + } + for(size_t i=0; i<mTriChannels.size(); i++) { + if (mTriChannels[i]->size() != tris) + errMsg("Tri channel size mismatch"); + } + if ((int)m1RingLookup.size() != nodes) + errMsg("1Ring size wrong"); + for(size_t t=0; t<mTris.size(); t++) { + if (taintedTris && taintedTris->find(t) != taintedTris->end()) continue; + for (int c=0; c<3; c++) { + int corner = t*3+c; + int node = mTris[t].c[c]; + int next = mTris[t].c[(c+1)%3]; + int prev = mTris[t].c[(c+2)%3]; + int rnext = mCorners[corner].next; + int rprev = mCorners[corner].prev; + int ro = mCorners[corner].opposite; + if (node < 0 || node >= nodes || next < 0 || next >= nodes || prev < 0 || prev >= nodes) + errMsg("invalid node entry"); + if (mCorners[corner].node != node || mCorners[corner].tri != (int)t) + errMsg("invalid basic corner entry"); + if (rnext < 0 || rnext >= corners || rprev < 0 || rprev >= corners || ro >= corners) + errMsg("invalid corner links"); + if (mCorners[rnext].node != next || mCorners[rprev].node != prev) + errMsg("invalid corner next/prev"); + if (strict && ro < 0) + errMsg("opposite missing"); + if (mCorners[ro].opposite != corner) + errMsg("invalid opposite ref"); + set<int>& rnodes = m1RingLookup[node].nodes; + set<int>& rtris = m1RingLookup[node].tris; + if (rnodes.find(next) == rnodes.end() || rnodes.find(prev) == rnodes.end()) { + debMsg("Tri "<< t << " " << node << " " << next << " " << prev , 1); + for(set<int>::iterator it= rnodes.begin(); it != rnodes.end(); ++it) + debMsg( *it , 1); + errMsg("node missing in 1ring"); + } + if (rtris.find(t) == rtris.end()) { + debMsg("Tri "<< t << " " << node , 1); + errMsg("tri missing in 1ring"); + } + } + } + for (int n=0; n<nodes; n++) { + bool docheck=true; + if (deletedNodes) + for (size_t e=0; e<deletedNodes->size(); e++) + if ((*deletedNodes)[e] == n) docheck=false;; + + if (docheck) { + set<int>& sn = m1RingLookup[n].nodes; + set<int>& st = m1RingLookup[n].tris; + set<int> sn2; + + for (set<int>::iterator it=st.begin(); it != st.end(); ++it) { + bool found = false; + for (int c=0; c<3; c++) { + if (mTris[*it].c[c] == n) + found = true; + else + sn2.insert(mTris[*it].c[c]); + } + if (!found) { + cout << *it << " " << n << endl; + for (int c=0; c<3; c++) cout << mTris[*it].c[c] << endl; + errMsg("invalid triangle in 1ring"); + } + if (taintedTris && taintedTris->find(*it) != taintedTris->end()) { + cout << *it << endl; + errMsg("tainted tri still is use"); + } + } + if (sn.size() != sn2.size()) + errMsg("invalid nodes in 1ring"); + for (set<int>::iterator it=sn.begin(), it2=sn2.begin(); it != sn.end(); ++it,++it2) { + if (*it != *it2) { + cout << "Node " << n << ": " << *it << " vs " << *it2 << endl; + errMsg("node ring mismatch"); + } + } + } + } +} + + void Mesh::SDFKernel(const int* partStart, const int* partLen, CVec3Ptr pos, CVec3Ptr normal, float* sdf, Vec3i gridRes, int intRadius, float safeRadius2, float cutoff2, float isigma2) + { + for (int cnt_x(0); cnt_x < gridRes[0]; ++cnt_x) { + for (int cnt_y(0); cnt_y < gridRes[1]; ++cnt_y) { + for (int cnt_z(0); cnt_z < gridRes[2]; ++cnt_z) { + // cell index, center + Vec3i cell = Vec3i(cnt_x, cnt_y, cnt_z); + if (cell.x >= gridRes.x || cell.y >= gridRes.y || cell.z >= gridRes.z) return; + Vec3 cpos = Vec3(cell.x + 0.5f, cell.y + 0.5f, cell.z + 0.5f); + float sum = 0.0f; + float dist = 0.0f; + + // query cells within block radius + Vec3i minBlock = Vec3i(max(cell.x - intRadius,0), max(cell.y - intRadius,0), max(cell.z - intRadius,0)); + Vec3i maxBlock = Vec3i(min(cell.x + intRadius, gridRes.x - 1), min(cell.y + intRadius, gridRes.y - 1), min(cell.z + intRadius, gridRes.z - 1)); + for (int i=minBlock.x; i<=maxBlock.x; i++) + for (int j=minBlock.y; j<=maxBlock.y; j++) + for (int k=minBlock.z; k<=maxBlock.z; k++) { + // test if block is within radius + Vec3 d = Vec3(cell.x-i, cell.y-j, cell.z-k); + float normSqr = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; + if (normSqr > safeRadius2) continue; + + // find source cell, and divide it into thread blocks + int block = i + gridRes.x * (j + gridRes.y * k); + int slen = partLen[block]; + if (slen == 0) continue; + int start = partStart[block]; + + // process sources + for(int s=0; s<slen; s++) { + + // actual sdf kernel + Vec3 r = cpos - pos.get(start+s); + float normSqr = r[0]*r[0] + r[1]*r[1] + r[2]*r[2]; + float r2 = normSqr; + if (r2 < cutoff2) { + float w = expf(-r2*isigma2); + sum += w; + dist += dot(normal.get(start+s), r) * w; + } + } + } + // writeback + if (sum > 0.0f) + sdf[cell.x + gridRes.x * (cell.y + gridRes.y * cell.z)] = dist / sum; + } + } + } + } + + inline int _cIndex(const Vec3& pos, const Vec3i& s) { + Vec3i p = toVec3i(pos); + if (p.x < 0 || p.y < 0 || p.z < 0 || p.x >= s.x || p.y >= s.y || p.z >= s.z) return -1; + return p.x + s.x * (p.y + s.y * p.z); + } + + //! Kernel: Apply a shape to a grid, setting value inside + +template <class T> struct ApplyMeshToGrid : public KernelBase { ApplyMeshToGrid(Grid<T>* grid, Grid<Real> sdf, T value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),sdf(sdf),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, Grid<T>* grid, Grid<Real> sdf, T value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (sdf(i,j,k) < 0) + { + (*grid)(i,j,k) = value; + } + } inline Grid<T>* getArg0() { return grid; } typedef Grid<T> type0;inline Grid<Real> & getArg1() { return sdf; } typedef Grid<Real> type1;inline T& getArg2() { return value; } typedef T type2;inline FlagGrid* getArg3() { return respectFlags; } typedef FlagGrid type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,sdf,value,respectFlags); } Grid<T>* grid; Grid<Real> sdf; T value; FlagGrid* respectFlags; }; + + void Mesh::applyToGrid(GridBase* grid, FlagGrid* respectFlags, float cutoff) { + FluidSolver dummy(grid->getSize()); + LevelsetGrid mesh_sdf(&dummy, false); + meshSDF(*this, mesh_sdf, 2., _args.get<float>("cutoff")); + + if (grid->getType() & GridBase::TypeInt) + ApplyMeshToGrid<int> ((Grid<int>*)grid, mesh_sdf, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyMeshToGrid<Real> ((Grid<Real>*)grid, mesh_sdf, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyMeshToGrid<Vec3> ((Grid<Vec3>*)grid, mesh_sdf, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGrid(): unknown grid type"); + } + + void Mesh::meshSDF(Mesh& mesh, LevelsetGrid & levelset, float sigma, float cutoff) + { + if (cutoff<0) cutoff = 2*sigma; + float maxEdgeLength = 0.75; + float numSamplesPerCell = 0.75; + + Vec3i gridRes = levelset.getSize(); + Vec3 mult = toVec3(gridRes) / toVec3(mesh.getParent()->getGridSize()); + + // prepare center values + std::vector<Vec3> center; + std::vector<Vec3> normals; + short bigEdges(0); + std::vector<Vec3> samplePoints; + for(size_t i=0; i<mesh.numTris(); i++){ + center.push_back(Vec3(mesh.getFaceCenter(i) * mult)); + normals.push_back(mesh.getFaceNormal(i)); + //count big, stretched edges + bigEdges = 0; + for (short edge(0); edge <3; ++edge){ + if(norm(mesh.getEdge(i,edge)) > maxEdgeLength){ + bigEdges += 1 << edge; + } + } + if(bigEdges > 0){ + samplePoints.clear(); + short iterA, pointA, iterB, pointB; + int numSamples0 = norm(mesh.getEdge(i,1)) * numSamplesPerCell; + int numSamples1 = norm(mesh.getEdge(i,2)) * numSamplesPerCell; + int numSamples2 = norm(mesh.getEdge(i,0)) * numSamplesPerCell; + if(! (bigEdges & (1 << 0))){ + //loop through 0,1 + iterA = numSamples1; + pointA = 0; + iterB = numSamples2; + pointB = 1; + } + else if(! (bigEdges & (1 << 1))){ + //loop through 1,2 + iterA = numSamples2; + pointA = 1; + iterB = numSamples0; + pointB = 2; + + } + else{ + //loop through 2,0 + iterA = numSamples0; + pointA = 2; + iterB = numSamples1; + pointB = 0; + } + + short axisA(0), axisB(1); + float u(0.),v(0.),w(0.); //barycentric uvw coords + Vec3 samplePoint,normal; + for (int sample0(0); sample0 < iterA; ++sample0){ + u = float(1. * sample0 / iterA); + for (int sample1(0); sample1 < iterB; ++sample1){ + v = float(1. * sample1 / iterB); + w = 1 - u - v; + if (w < 0.) + continue; + samplePoint = mesh.getNode(i,pointA) * mult * u + + mesh.getNode(i,pointB) * mult * v + + mesh.getNode(i,(3 - pointA - pointB)) * mult * w; + samplePoints.push_back(samplePoint); + normal = mesh.getFaceNormal(i); + normals.push_back(normal); + } + } + center.insert(center.end(), samplePoints.begin(), samplePoints.end()); + } + } + // prepare grid + const int numCells = gridRes.x * gridRes.y * gridRes.z; + vector<Real> gridDev(numCells); + for (int i=0; i<numCells; i++) + gridDev[i] = -cutoff; + + // 1. count sources per cell + vector<int> srcPerCell(numCells); + for (size_t i=0; i<center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell >= 0) + srcPerCell[cell]++; + } + + // 2. create start index lookup + vector<int> srcCellStart(numCells); + int cnt=0; + for (int i=0; i<numCells; i++) { + srcCellStart[i] = cnt; + cnt += srcPerCell[i]; + } + + // 3. reorder nodes + CVec3Array reorderPos(center.size()); + CVec3Array reorderNormal(center.size()); + { + vector<int> curSrcCell(numCells); + for (int i=0; i<(int)center.size(); i++) { + int cell = _cIndex(center[i], gridRes); + if (cell < 0) continue; + int idx = srcCellStart[cell] + curSrcCell[cell]; + reorderPos.set(idx, center[i]); + reorderNormal.set(idx, normals[i]); + curSrcCell[cell]++; + } + } + + // construct parameters + float safeRadius = cutoff + sqrt(3.0)*0.5; + float safeRadius2 = safeRadius*safeRadius; + float cutoff2 = cutoff*cutoff; + float isigma2 = 1.0/(sigma*sigma); + int intRadius = (int)(cutoff+0.5); + + SDFKernel(srcCellStart.data(), srcPerCell.data(), + reorderPos.data(), reorderNormal.data(), + gridDev.data(), + Vec3i(gridRes.x, gridRes.y, gridRes.z), intRadius, safeRadius2, cutoff2, isigma2); + + for (int i=0;i<numCells; i++) + levelset[i] = gridDev[i]; + + // floodfill outside + std::stack<Vec3i> outside; + FOR_IJK(levelset) { + if (levelset(i,j,k) >= cutoff-1.0f) + outside.push(Vec3i(i,j,k)); + } + while(!outside.empty()) { + Vec3i c = outside.top(); + outside.pop(); + levelset(c) = cutoff; + if (c.x > 0 && levelset(c.x-1, c.y, c.z) < 0) outside.push(Vec3i(c.x-1,c.y,c.z)); + if (c.y > 0 && levelset(c.x, c.y-1, c.z) < 0) outside.push(Vec3i(c.x,c.y-1,c.z)); + if (c.z > 0 && levelset(c.x, c.y, c.z-1) < 0) outside.push(Vec3i(c.x,c.y,c.z-1)); + if (c.x < levelset.getSizeX()-1 && levelset(c.x+1, c.y, c.z) < 0) outside.push(Vec3i(c.x+1,c.y,c.z)); + if (c.y < levelset.getSizeY()-1 && levelset(c.x, c.y+1, c.z) < 0) outside.push(Vec3i(c.x,c.y+1,c.z)); + if (c.z < levelset.getSizeZ()-1 && levelset(c.x, c.y, c.z+1) < 0) outside.push(Vec3i(c.x,c.y,c.z+1)); + }; + } + +} //namespace + + diff --git a/source/blender/python/manta_pp/mesh.h b/source/blender/python/manta_pp/mesh.h new file mode 100644 index 00000000000..7917fefcaf1 --- /dev/null +++ b/source/blender/python/manta_pp/mesh.h @@ -0,0 +1,273 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Meshes + * + * note: this is only a temporary solution, details are bound to change + * long term goal is integration with Split&Merge code by Wojtan et al. + * + ******************************************************************************/ + +#ifndef _MESH_H +#define _MESH_H + +#include <vector> +#include "manta.h" +#include "grid.h" +#include "vectorbase.h" +#include <set> +namespace Manta { + +// fwd decl +class FlagGrid; +class MACGrid; +class Shape; + +//! Node position and flags +struct Node { + Node() : flags(0), pos(Vec3::Zero), normal(Vec3::Zero) {} + Node(const Vec3& p) : flags(0), pos(p) {} + int flags; + Vec3 pos, normal; +}; + +//! Carries indices of its nodes +struct Triangle { + Triangle() : flags(0) { c[0] = c[1] = c[2] = 0; } + Triangle(int n0, int n1, int n2) : flags(0) { c[0]=n0; c[1]=n1; c[2]=n2; } + + int c[3]; + int flags; +}; + +//! For fast access to nodes and neighboring triangles +struct Corner { + Corner() : tri(-1), node(-1), opposite(-1), next(-1), prev(-1) {}; + Corner(int t, int n) : tri(t), node(n), opposite(-1), next(-1), prev(-1) {} + + int tri; + int node; + int opposite; + int next; + int prev; +}; + +//! Base class for mesh data channels (texture coords, vorticity, ...) +struct NodeChannel { + virtual ~NodeChannel() {}; + virtual void resize(int num) = 0; + virtual int size() = 0; + virtual NodeChannel* clone() = 0; + + virtual void addInterpol(int a, int b, Real alpha) = 0; + virtual void mergeWith(int node, int delnode, Real alpha) = 0; + virtual void renumber(const std::vector<int>& newIndex, int newsize) = 0; +}; + +//! Node channel using only a vector +template<class T> +struct SimpleNodeChannel : public NodeChannel { + SimpleNodeChannel() {}; + SimpleNodeChannel(const SimpleNodeChannel<T>& a) : data(a.data) {} + void resize(int num) { data.resize(num); } + virtual int size() { return data.size(); } + virtual void renumber(const std::vector<int>& newIndex, int newsize); + + //virtual void addSplit(int from, Real alpha) { data.push_back(data[from]); } + + std::vector<T> data; +}; + +//! Base class for mesh data channels (texture coords, vorticity, ...) +struct TriChannel { + virtual ~TriChannel() {}; + virtual void resize(int num) = 0; + virtual TriChannel* clone() = 0; + virtual int size() = 0; + + virtual void addNew() = 0; + virtual void addSplit(int from, Real alpha) = 0; + virtual void remove(int tri) = 0; +}; + +//! Tri channel using only a vector +template<class T> +struct SimpleTriChannel : public TriChannel { + SimpleTriChannel() {}; + SimpleTriChannel(const SimpleTriChannel<T>& a) : data(a.data) {} + void resize(int num) { data.resize(num); } + void remove(int tri) { if (tri!=(int)data.size()-1) data[tri] = *data.rbegin(); data.pop_back(); } + virtual int size() { return data.size(); } + + virtual void addSplit(int from, Real alpha) { data.push_back(data[from]); } + virtual void addNew() { data.push_back(T()); } + + std::vector<T> data; +}; + +struct OneRing { + OneRing() {} + std::set<int> nodes; + std::set<int> tris; +}; + +/*!adapted from Cudatools.h +*/ +struct CVec3Ptr { + float *x, *y, *z; + inline Vec3 get(int i) const { return Vec3(x[i],y[i],z[i]); }; + inline void set(int i, const Vec3& v) { x[i]=v.x; y[i]=v.y; z[i]=v.z; }; +}; + +struct CVec3Array { + CVec3Array(int sz) { + x.resize(sz); + y.resize(sz); + z.resize(sz); + } + CVec3Array(const std::vector<Vec3>& v) { + x.resize(v.size()); + y.resize(v.size()); + z.resize(v.size()); + for (size_t i=0; i<v.size(); i++) { + x[i] = v[i].x; + y[i] = v[i].y; + z[i] = v[i].z; + } + } + CVec3Ptr data() { + CVec3Ptr a = { x.data(), y.data(), z.data()}; + return a; + } + inline const Vec3 operator[](int idx) const { return Vec3((Real)x[idx], (Real)y[idx], (Real)z[idx]); } + inline void set(int idx, const Vec3& v) { x[idx] = v.x; y[idx] = v.y; z[idx] = v.z; } + inline int size() { return x.size(); } + std::vector<float> x, y, z; +}; + +//! Triangle mesh class +/*! note: this is only a temporary solution, details are bound to change + long term goal is integration with Split&Merge code by Wojtan et al.*/ +class Mesh : public PbClass {public: + Mesh(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Mesh::Mesh" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new Mesh(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Mesh::Mesh" ); return 0; } catch(std::exception& e) { pbSetError("Mesh::Mesh",e.what()); return -1; } } + virtual ~Mesh(); + virtual Mesh* clone(); + + enum NodeFlags { NfNone = 0, NfFixed = 1, NfMarked = 2, NfKillme = 4, NfCollide = 8 }; + enum FaceFlags { FfNone = 0, FfDoubled = 1, FfMarked = 2 }; + enum MeshType { TypeNormal = 0, TypeVortexSheet }; + + virtual MeshType getType() { return TypeNormal; } + + void clear(); + Real computeCenterOfMass(Vec3& cm) const; + void computeVertexNormals(); + + // plugins + void load(std::string name, bool append = false); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::load"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); bool append = _args.getOpt<bool >("append",1,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->load(name,append); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::load"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::load",e.what()); return 0; } } + void fromShape(Shape& shape, bool append = false); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::fromShape"); PyObject *_retval = 0; { ArgLocker _lock; Shape& shape = *_args.getPtr<Shape >("shape",0,&_lock); bool append = _args.getOpt<bool >("append",1,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->fromShape(shape,append); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::fromShape"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::fromShape",e.what()); return 0; } } + void save(std::string name); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::save"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->save(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::save"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::save",e.what()); return 0; } } + void advectInGrid(FlagGrid& flaggrid, MACGrid& vel, int integrationMode); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::advectInGrid"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flaggrid = *_args.getPtr<FlagGrid >("flaggrid",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); int integrationMode = _args.get<int >("integrationMode",2,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->advectInGrid(flaggrid,vel,integrationMode); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::advectInGrid"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::advectInGrid",e.what()); return 0; } } + void scale(Vec3 s); static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::scale"); PyObject *_retval = 0; { ArgLocker _lock; Vec3 s = _args.get<Vec3 >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->scale(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::scale"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::scale",e.what()); return 0; } } + void offset(Vec3 o); static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::offset"); PyObject *_retval = 0; { ArgLocker _lock; Vec3 o = _args.get<Vec3 >("o",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->offset(o); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::offset"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::offset",e.what()); return 0; } } + + // ops + Mesh& operator=(const Mesh& o); + + // accessors + inline int numTris() const { return mTris.size(); } + inline int numNodes() const { return mNodes.size(); } + inline int numTriChannels() const { return mTriChannels.size(); } + inline int numNodeChannels() const { return mNodeChannels.size(); } + + inline Triangle& tris(int i) { return mTris[i]; } + inline Node& nodes(int i) { return mNodes[i]; } + inline Corner& corners(int tri, int c) { return mCorners[tri*3+c]; } + inline Corner& corners(int c) { return mCorners[c]; } + inline NodeChannel* nodeChannel(int i) { return mNodeChannels[i]; } + inline TriChannel* triChannel(int i) { return mTriChannels[i]; } + + // allocate memory (eg upon load) + void resizeTris(int numTris); + void resizeNodes(int numNodes); + + inline bool isNodeFixed(int n) { return mNodes[n].flags & NfFixed; } + inline bool isTriangleFixed(int t) { return (mNodes[mTris[t].c[0]].flags & NfFixed) || (mNodes[mTris[t].c[1]].flags & NfFixed) || (mNodes[mTris[t].c[2]].flags & NfFixed); } + + inline const Vec3 getNode(int tri, int c) const { return mNodes[mTris[tri].c[c]].pos; } + inline Vec3& getNode(int tri, int c) { return mNodes[mTris[tri].c[c]].pos; } + inline const Vec3 getEdge(int tri, int e) const { return getNode(tri,(e+1)%3) - getNode(tri,e); } + inline OneRing& get1Ring(int node) { return m1RingLookup[node]; } + inline Real getFaceArea(int t) { Vec3 c0 = mNodes[mTris[t].c[0]].pos; return 0.5*norm(cross(mNodes[mTris[t].c[1]].pos - c0, mNodes[mTris[t].c[2]].pos - c0)); } + inline Vec3 getFaceNormal(int t) { Vec3 c0 = mNodes[mTris[t].c[0]].pos; return getNormalized(cross(mNodes[mTris[t].c[1]].pos - c0, mNodes[mTris[t].c[2]].pos - c0)); } + inline Vec3 getFaceCenter(int t) { return (mNodes[mTris[t].c[0]].pos + mNodes[mTris[t].c[1]].pos + mNodes[mTris[t].c[2]].pos) / 3.0; } + inline std::vector<Node>& getNodeData() { return mNodes; } + + void mergeNode(int node, int delnode); + int addNode(Node a); + int addTri(Triangle a); + void addCorner(Corner a); + void removeTri(int tri); + void removeTriFromLookup(int tri); + void removeNodes(const std::vector<int>& deletedNodes); + void rebuildCorners(int from=0, int to=-1); + void rebuildLookup(int from=0, int to=-1); + void rebuildQuickCheck(); + void fastNodeLookupRebuild(int corner); + void sanityCheck(bool strict=true, std::vector<int>* deletedNodes=0, std::map<int,bool>* taintedTris=0); + + void addTriChannel(TriChannel* c) { mTriChannels.push_back(c); rebuildChannels(); } + void addNodeChannel(NodeChannel* c) { mNodeChannels.push_back(c); rebuildChannels(); } + void SDFKernel(const int* partStart, const int* partLen, CVec3Ptr pos, CVec3Ptr normal, float* sdf, Vec3i gridRes, int intRadius, float safeRadius2, float cutoff2, float isigma2); + void meshSDF(Mesh& mesh, LevelsetGrid& levelset, float sigma, float cutoff=-1); static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::meshSDF"); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); LevelsetGrid& levelset = *_args.getPtr<LevelsetGrid >("levelset",1,&_lock); float sigma = _args.get<float >("sigma",2,&_lock); float cutoff = _args.getOpt<float >("cutoff",3,-1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->meshSDF(mesh,levelset,sigma,cutoff); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::meshSDF"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::meshSDF",e.what()); return 0; } } + void applyToGrid(GridBase* grid, FlagGrid* respectFlags=0, float cutoff=-1); static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Mesh* pbo = dynamic_cast<Mesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Mesh::applyToGrid"); PyObject *_retval = 0; { ArgLocker _lock; GridBase* grid = _args.getPtr<GridBase >("grid",0,&_lock); FlagGrid* respectFlags = _args.getPtrOpt<FlagGrid >("respectFlags",1,0,&_lock); float cutoff = _args.getOpt<float >("cutoff",2,-1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->applyToGrid(grid,respectFlags,cutoff); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Mesh::applyToGrid"); return _retval; } catch(std::exception& e) { pbSetError("Mesh::applyToGrid",e.what()); return 0; } } + +protected: + void rebuildChannels(); + + std::vector<Node> mNodes; + std::vector<Triangle> mTris; + std::vector<Corner> mCorners; + std::vector<NodeChannel*> mNodeChannels; + std::vector<TriChannel*> mTriChannels; std::vector<OneRing> m1RingLookup; public: PbArgs _args;} +#define _C_Mesh +; + + + + +// *************************************************************************************************************** +// Implementation + +template<class T> +void SimpleNodeChannel<T>::renumber(const std::vector<int>& newIndex, int newsize) { + for(size_t i=0; i<newIndex.size(); i++) { + if(newIndex[i]!=-1) + data[newIndex[i]] = data[newsize+i]; + } + data.resize(newsize); +} + + + +} //namespace +#endif + + diff --git a/source/blender/python/manta_pp/mesh.h.reg b/source/blender/python/manta_pp/mesh.h.reg new file mode 100644 index 00000000000..fa2e455d9d0 --- /dev/null +++ b/source/blender/python/manta_pp/mesh.h.reg @@ -0,0 +1,12 @@ +#include "mesh.h" ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","Mesh","PbClass"); template<> const char* Namify<Mesh >::S = "Mesh"; +>Mesh^ ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","Mesh",Mesh::_W_0); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","load",Mesh::_W_1); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","fromShape",Mesh::_W_2); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","save",Mesh::_W_3); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","advectInGrid",Mesh::_W_4); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","scale",Mesh::_W_5); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","offset",Mesh::_W_6); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","meshSDF",Mesh::_W_7); ++Mesh^ static const Pb::Register _R_$IDX$ ("Mesh","applyToGrid",Mesh::_W_8); diff --git a/source/blender/python/manta_pp/mesh.h.reg.cpp b/source/blender/python/manta_pp/mesh.h.reg.cpp new file mode 100644 index 00000000000..37118fcc6c7 --- /dev/null +++ b/source/blender/python/manta_pp/mesh.h.reg.cpp @@ -0,0 +1,26 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "mesh.h" +namespace Manta { +#ifdef _C_Mesh + static const Pb::Register _R_0 ("Mesh","Mesh","PbClass"); template<> const char* Namify<Mesh >::S = "Mesh"; + static const Pb::Register _R_1 ("Mesh","Mesh",Mesh::_W_0); + static const Pb::Register _R_2 ("Mesh","load",Mesh::_W_1); + static const Pb::Register _R_3 ("Mesh","fromShape",Mesh::_W_2); + static const Pb::Register _R_4 ("Mesh","save",Mesh::_W_3); + static const Pb::Register _R_5 ("Mesh","advectInGrid",Mesh::_W_4); + static const Pb::Register _R_6 ("Mesh","scale",Mesh::_W_5); + static const Pb::Register _R_7 ("Mesh","offset",Mesh::_W_6); + static const Pb::Register _R_8 ("Mesh","meshSDF",Mesh::_W_7); + static const Pb::Register _R_9 ("Mesh","applyToGrid",Mesh::_W_8); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/movingobs.cpp b/source/blender/python/manta_pp/movingobs.cpp new file mode 100644 index 00000000000..2893a4fe25e --- /dev/null +++ b/source/blender/python/manta_pp/movingobs.cpp @@ -0,0 +1,105 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Moving obstacles + * + ******************************************************************************/ + +#include "movingobs.h" +#include "commonkernels.h" +#include "randomstream.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// MovingObs class members + +int MovingObstacle::sIDcnt = 10; + +MovingObstacle::MovingObstacle (FluidSolver* parent, int emptyType) + : PbClass(parent), mEmptyType(emptyType) +{ + mID = 1<<sIDcnt; + sIDcnt++; + if (sIDcnt > 15) + errMsg ("currently only 5 separate moving obstacles supported (are you generating them in a loop?)"); +} + +void MovingObstacle::add(Shape* shape) { + mShapes.push_back(shape); +} + +void MovingObstacle::projectOutside(FlagGrid& flags, BasicParticleSystem& parts) { + LevelsetGrid levelset(mParent,false); + Grid<Vec3> gradient(mParent); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + // build levelset gradient + GradientOp(gradient, levelset); + + parts.projectOutside(gradient); +} + +void MovingObstacle::moveLinear(Real t, Real t0, Real t1, Vec3 p0, Vec3 p1, FlagGrid& flags, MACGrid& vel, bool smooth) { + Real alpha = (t-t0)/(t1-t0); + if (alpha >= 0 && alpha <= 1) { + Vec3 v = (p1-p0) / ((t1-t0)* getParent()->getDt() ); + + // ease in and out + if (smooth) { + v *= 6.0f*(alpha-square(alpha)); + alpha = square(alpha)*(3.0f-2.0f*alpha); + } + + Vec3 pos = alpha * p1 + (1.0f-alpha) * p0; + for (size_t i=0; i<mShapes.size(); i++) + mShapes[i]->setCenter(pos); + + // reset flags + FOR_IDX(flags) { + if ((flags[idx] & mID) != 0) + flags[idx] = mEmptyType; + } + // apply new flags + for (size_t i=0; i<mShapes.size(); i++) { + mShapes[i]->_args.clear(); + mShapes[i]->_args.add("value", FlagGrid::TypeObstacle | mID); + mShapes[i]->applyToGrid(&flags, 0); + } + // apply velocities + FOR_IJK_BND(flags,1) { + bool cur = (flags(i,j,k) & mID) != 0; + if (cur || (flags(i-1,j,k) & mID) != 0) vel(i,j,k).x = v.x; + if (cur || (flags(i,j-1,k) & mID) != 0) vel(i,j,k).y = v.y; + if (cur || (flags(i,j,k-1) & mID) != 0) vel(i,j,k).z = v.z; + } + } +} + + +} //namespace + + diff --git a/source/blender/python/manta_pp/movingobs.h b/source/blender/python/manta_pp/movingobs.h new file mode 100644 index 00000000000..6ffcc59cf40 --- /dev/null +++ b/source/blender/python/manta_pp/movingobs.h @@ -0,0 +1,54 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * moving obstacles + * + ******************************************************************************/ + +#ifndef _MOVINGOBS_H +#define _MOVINGOBS_H + +#include "shapes.h" +#include "particle.h" + +namespace Manta { + +//! Moving obstacle composed of basic shapes +class MovingObstacle : public PbClass {public: + MovingObstacle(FluidSolver* parent, int emptyType=FlagGrid::TypeEmpty); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "MovingObstacle::MovingObstacle" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); int emptyType = _args.getOpt<int >("emptyType",1,FlagGrid::TypeEmpty,&_lock); obj = new MovingObstacle(parent,emptyType); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"MovingObstacle::MovingObstacle" ); return 0; } catch(std::exception& e) { pbSetError("MovingObstacle::MovingObstacle",e.what()); return -1; } } + + void add(Shape* shape); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); MovingObstacle* pbo = dynamic_cast<MovingObstacle*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "MovingObstacle::add"); PyObject *_retval = 0; { ArgLocker _lock; Shape* shape = _args.getPtr<Shape >("shape",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->add(shape); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"MovingObstacle::add"); return _retval; } catch(std::exception& e) { pbSetError("MovingObstacle::add",e.what()); return 0; } } + //! If t in [t0,t1], apply linear motion path from p0 to p1 + void moveLinear(Real t, Real t0, Real t1, Vec3 p0, Vec3 p1, FlagGrid& flags, MACGrid& vel, bool smooth=true); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); MovingObstacle* pbo = dynamic_cast<MovingObstacle*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "MovingObstacle::moveLinear"); PyObject *_retval = 0; { ArgLocker _lock; Real t = _args.get<Real >("t",0,&_lock); Real t0 = _args.get<Real >("t0",1,&_lock); Real t1 = _args.get<Real >("t1",2,&_lock); Vec3 p0 = _args.get<Vec3 >("p0",3,&_lock); Vec3 p1 = _args.get<Vec3 >("p1",4,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",5,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",6,&_lock); bool smooth = _args.getOpt<bool >("smooth",7,true,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->moveLinear(t,t0,t1,p0,p1,flags,vel,smooth); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"MovingObstacle::moveLinear"); return _retval; } catch(std::exception& e) { pbSetError("MovingObstacle::moveLinear",e.what()); return 0; } } + //! Compute levelset, and project FLIP particles outside obstacles + void projectOutside(FlagGrid& flags, BasicParticleSystem& flip); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); MovingObstacle* pbo = dynamic_cast<MovingObstacle*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "MovingObstacle::projectOutside"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); BasicParticleSystem& flip = *_args.getPtr<BasicParticleSystem >("flip",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->projectOutside(flags,flip); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"MovingObstacle::projectOutside"); return _retval; } catch(std::exception& e) { pbSetError("MovingObstacle::projectOutside",e.what()); return 0; } } + +protected: + std::vector<Shape*> mShapes; + int mEmptyType; + int mID; static int sIDcnt; public: PbArgs _args;} +#define _C_MovingObstacle +; + + +} //namespace +#endif + + diff --git a/source/blender/python/manta_pp/movingobs.h.reg b/source/blender/python/manta_pp/movingobs.h.reg new file mode 100644 index 00000000000..7c95949a6de --- /dev/null +++ b/source/blender/python/manta_pp/movingobs.h.reg @@ -0,0 +1,7 @@ +#include "movingobs.h" ++MovingObstacle^ static const Pb::Register _R_$IDX$ ("MovingObstacle","MovingObstacle","PbClass"); template<> const char* Namify<MovingObstacle >::S = "MovingObstacle"; +>MovingObstacle^ ++MovingObstacle^ static const Pb::Register _R_$IDX$ ("MovingObstacle","MovingObstacle",MovingObstacle::_W_0); ++MovingObstacle^ static const Pb::Register _R_$IDX$ ("MovingObstacle","add",MovingObstacle::_W_1); ++MovingObstacle^ static const Pb::Register _R_$IDX$ ("MovingObstacle","moveLinear",MovingObstacle::_W_2); ++MovingObstacle^ static const Pb::Register _R_$IDX$ ("MovingObstacle","projectOutside",MovingObstacle::_W_3); diff --git a/source/blender/python/manta_pp/movingobs.h.reg.cpp b/source/blender/python/manta_pp/movingobs.h.reg.cpp new file mode 100644 index 00000000000..4e0dbd8ba95 --- /dev/null +++ b/source/blender/python/manta_pp/movingobs.h.reg.cpp @@ -0,0 +1,21 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "movingobs.h" +namespace Manta { +#ifdef _C_MovingObstacle + static const Pb::Register _R_0 ("MovingObstacle","MovingObstacle","PbClass"); template<> const char* Namify<MovingObstacle >::S = "MovingObstacle"; + static const Pb::Register _R_1 ("MovingObstacle","MovingObstacle",MovingObstacle::_W_0); + static const Pb::Register _R_2 ("MovingObstacle","add",MovingObstacle::_W_1); + static const Pb::Register _R_3 ("MovingObstacle","moveLinear",MovingObstacle::_W_2); + static const Pb::Register _R_4 ("MovingObstacle","projectOutside",MovingObstacle::_W_3); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/noisefield.cpp b/source/blender/python/manta_pp/noisefield.cpp new file mode 100644 index 00000000000..9fad3d4b286 --- /dev/null +++ b/source/blender/python/manta_pp/noisefield.cpp @@ -0,0 +1,309 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Noise field + * + ******************************************************************************/ + +#include "noisefield.h" +#include "randomstream.h" +#include "grid.h" + +using namespace std; + +//***************************************************************************** +// Wavelet noise + +#if FLOATINGPOINT_PRECISION==1 +#define TILENAME "waveletNoiseTile.bin" +#else +#define TILENAME "waveletNoiseTileD.bin" +#endif + +namespace Manta { + +int WaveletNoiseField::randomSeed = 13322223; +Real* WaveletNoiseField::mNoiseTile = NULL; + +static Real _aCoeffs[32] = { + 0.000334,-0.001528, 0.000410, 0.003545,-0.000938,-0.008233, 0.002172, 0.019120, + -0.005040,-0.044412, 0.011655, 0.103311,-0.025936,-0.243780, 0.033979, 0.655340, + 0.655340, 0.033979,-0.243780,-0.025936, 0.103311, 0.011655,-0.044412,-0.005040, + 0.019120, 0.002172,-0.008233,-0.000938, 0.003546, 0.000410,-0.001528, 0.000334}; + +void WaveletNoiseField::downsample(Real *from, Real *to, int n, int stride){ + const Real *a = &_aCoeffs[16]; + for (int i = 0; i < n / 2; i++) { + to[i * stride] = 0; + for (int k = 2 * i - 16; k < 2 * i + 16; k++) { + to[i * stride] += a[k - 2 * i] * from[modFast128(k) * stride]; + } + } +} + +static Real _pCoeffs[4] = {0.25, 0.75, 0.75, 0.25}; + +void WaveletNoiseField::upsample(Real *from, Real *to, int n, int stride) { + const Real *pp = &_pCoeffs[1]; + + for (int i = 0; i < n; i++) { + to[i * stride] = 0; + for (int k = i / 2 - 1 ; k < i / 2 + 3; k++) { + to[i * stride] += 0.5 * pp[k - i / 2] * from[modSlow(k, n / 2) * stride]; + } // new */ + } +} + +WaveletNoiseField::WaveletNoiseField(FluidSolver* parent, int fixedSeed, int loadFromFile) : + PbClass(parent), mPosOffset(0.), mPosScale(1.), mValOffset(0.), mValScale(1.), mClamp(false), + mClampNeg(0), mClampPos(1), mTimeAnim(0), mGsInvX(0), mGsInvY(0), mGsInvZ(0) +{ + Real scale = 1.0/parent->getGridSize().max(); + mGsInvX = scale; + mGsInvY = scale; + mGsInvZ = parent->is3D() ? scale : 1; + + // use global random seed with offset if none is given + if (fixedSeed==-1) { + fixedSeed = randomSeed + 123; + } + RandomStream randStreamPos(fixedSeed); + mSeedOffset = Vec3( randStreamPos.getVec3Norm() ); + + generateTile( loadFromFile ); +}; + +string WaveletNoiseField::toString() { + std::ostringstream out; + out << "NoiseField: name '"<<mName<<"' "<< + " pos off="<<mPosOffset<<" scale="<<mPosScale<< + " val off="<<mValOffset<<" scale="<<mValScale<< + " clamp ="<<mClamp<<" val="<<mClampNeg<<" to "<<mClampPos<< + " timeAni ="<<mTimeAnim<< + " gridInv ="<<Vec3(mGsInvX,mGsInvY,mGsInvZ) ; + return out.str(); +} + +void WaveletNoiseField::generateTile( int loadFromFile) { + // generate tile + const int n = NOISE_TILE_SIZE; + const int n3 = n*n*n, n3d=n3*3; + + if(mNoiseTile) return; + Real *noise3 = new Real[n3d]; + if(loadFromFile) { + FILE* fp = fopen(TILENAME,"rb"); + if(fp) { + fread(noise3, sizeof(Real), n3d, fp); + fclose(fp); + debMsg("noise tile loaded from file! " , 1); + mNoiseTile = noise3; + return; + } + } + + debMsg("generating 3x " << n << "^3 noise tile " , 1); + Real *temp13 = new Real[n3d]; + Real *temp23 = new Real[n3d]; + + // initialize + for (int i = 0; i < n3d; i++) { + temp13[i] = temp23[i] = + noise3[i] = 0.; + } + + // Step 1. Fill the tile with random numbers in the range -1 to 1. + RandomStream randStreamTile ( randomSeed ); + for (int i = 0; i < n3d; i++) { + //noise3[i] = (randStream.getReal() + randStream2.getReal()) -1.; // produces repeated values?? + noise3[i] = randStreamTile.getRandNorm(0,1); + } + + // Steps 2 and 3. Downsample and upsample the tile + for (int tile=0; tile < 3; tile++) { + for (int iy = 0; iy < n; iy++) + for (int iz = 0; iz < n; iz++) { + const int i = iy * n + iz*n*n + tile*n3; + downsample(&noise3[i], &temp13[i], n, 1); + upsample (&temp13[i], &temp23[i], n, 1); + } + for (int ix = 0; ix < n; ix++) + for (int iz = 0; iz < n; iz++) { + const int i = ix + iz*n*n + tile*n3; + downsample(&temp23[i], &temp13[i], n, n); + upsample (&temp13[i], &temp23[i], n, n); + } + for (int ix = 0; ix < n; ix++) + for (int iy = 0; iy < n; iy++) { + const int i = ix + iy*n + tile*n3; + downsample(&temp23[i], &temp13[i], n, n*n); + upsample (&temp13[i], &temp23[i], n, n*n); + } + } + + // Step 4. Subtract out the coarse-scale contribution + for (int i = 0; i < n3d; i++) { + noise3[i] -= temp23[i]; + } + + // Avoid even/odd variance difference by adding odd-offset version of noise to itself. + int offset = n / 2; + if (offset % 2 == 0) offset++; + + if (n != 128) errMsg("WaveletNoise::Fast 128 mod used, change for non-128 resolution"); + + int icnt=0; + for (int tile=0; tile<3; tile++) + for (int ix = 0; ix < n; ix++) + for (int iy = 0; iy < n; iy++) + for (int iz = 0; iz < n; iz++) { + temp13[icnt] = noise3[modFast128(ix+offset) + modFast128(iy+offset)*n + modFast128(iz+offset)*n*n + tile*n3]; + icnt++; + } + + + for (int i = 0; i < n3d; i++) { + noise3[i] += temp13[i]; + } + + mNoiseTile = noise3; + delete[] temp13; + delete[] temp23; + + if(loadFromFile) { + FILE* fp = fopen(TILENAME,"wb"); + if(fp) { + fwrite(noise3, sizeof(Real), n3d, fp); + fclose(fp); + debMsg( "saved to file! " , 1); + } + } +} + + + +void WaveletNoiseField::downsampleNeumann(const Real *from, Real *to, int n, int stride) +{ + // if these values are not local incorrect results are generated + static const Real *const aCoCenter= &_aCoeffs[16]; + for (int i = 0; i < n / 2; i++) { + to[i * stride] = 0; + for (int k = 2 * i - 16; k < 2 * i + 16; k++) { + // handle boundary + Real fromval; + if (k < 0) { + fromval = from[0]; + } else if(k > n - 1) { + fromval = from[(n - 1) * stride]; + } else { + fromval = from[k * stride]; + } + to[i * stride] += aCoCenter[k - 2 * i] * fromval; + } + } +} + +void WaveletNoiseField::upsampleNeumann(const Real *from, Real *to, int n, int stride) { + static const Real *const pp = &_pCoeffs[1]; + for (int i = 0; i < n; i++) { + to[i * stride] = 0; + for (int k = i / 2 - 1 ; k < i / 2 + 3; k++) { + Real fromval; + if(k>n/2-1) { + fromval = from[(n/2-1) * stride]; + } else if(k < 0) { + fromval = from[0]; + } else { + fromval = from[k * stride]; + } + to[i * stride] += 0.5 * pp[k - i / 2] * fromval; + } + } +} + +void WaveletNoiseField::computeCoefficients(Grid<Real>& input, Grid<Real>& tempIn1, Grid<Real>& tempIn2) +{ + // generate tile + const int sx = input.getSizeX(); + const int sy = input.getSizeY(); + const int sz = input.getSizeZ(); + const int n3 = sx*sy*sz; + // just for compatibility with wavelet turb code + Real *temp13 = &tempIn1(0,0,0); + Real *temp23 = &tempIn2(0,0,0); + Real *noise3 = &input(0,0,0); + + // clear grids + for (int i = 0; i < n3; i++) { + temp13[i] = temp23[i] = 0.f; + } + + // Steps 2 and 3. Downsample and upsample the tile + for (int iz = 0; iz < sz; iz++) + for (int iy = 0; iy < sy; iy++) + { + const int i = iz*sx*sy + iy*sx; + downsampleNeumann(&noise3[i], &temp13[i], sx, 1 ); + upsampleNeumann (&temp13[i], &temp23[i], sx, 1); + } + + for (int iz = 0; iz < sz; iz++) + for (int ix = 0; ix < sx; ix++) + { + const int i = iz*sx*sy + ix; + downsampleNeumann(&temp23[i], &temp13[i], sy, sx ); + upsampleNeumann (&temp13[i], &temp23[i], sy, sx ); + } + + if(input.is3D()) { + for (int iy = 0; iy < sy; iy++) + for (int ix = 0; ix < sx; ix++) + { + const int i = iy*sx+ix; + downsampleNeumann(&temp23[i], &temp13[i], sz, sy*sx ); + upsampleNeumann (&temp13[i], &temp23[i], sz, sy*sx ); + } + } + + // Step 4. Subtract out the coarse-scale contribution + for (int i = 0; i < n3; i++) { + Real residual = noise3[i] - temp23[i]; + temp13[i] = sqrtf( fabs(residual) ); + } + + // copy back, and compute actual weight for wavelet turbulence... + Real smoothingFactor = 1./6.; + if(!input.is3D()) smoothingFactor = 1./4.; + FOR_IJK_BND(input,1) { + // apply some brute force smoothing + Real res = temp13[k*sx*sy+j*sx+i-1] + temp13[k*sx*sy+j*sx+i+1]; + res += temp13[k*sx*sy+j*sx+i-sx] + temp13[k*sx*sy+j*sx+i+sx]; + if( input.is3D()) res += temp13[k*sx*sy+j*sx+i-sx*sy] + temp13[k*sx*sy+j*sx+i+sx*sy]; + input(i,j,k) = res * smoothingFactor; + } +} + + + + + +} + + diff --git a/source/blender/python/manta_pp/noisefield.h b/source/blender/python/manta_pp/noisefield.h new file mode 100644 index 00000000000..c0e69d61dea --- /dev/null +++ b/source/blender/python/manta_pp/noisefield.h @@ -0,0 +1,405 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Wavelet noise field + * + ******************************************************************************/ + +#ifndef _NOISEFIELD_H_ +#define _NOISEFIELD_H_ + +#include "vectorbase.h" +#include "manta.h" + +namespace Manta { + +#define NOISE_TILE_SIZE 128 + +// wrapper for a parametrized field of wavelet noise + +class WaveletNoiseField : public PbClass { public: + WaveletNoiseField( FluidSolver* parent, int fixedSeed=-1 , int loadFromFile=false ); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "WaveletNoiseField::WaveletNoiseField" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); int fixedSeed = _args.getOpt<int >("fixedSeed",1,-1 ,&_lock); int loadFromFile = _args.getOpt<int >("loadFromFile",2,false ,&_lock); obj = new WaveletNoiseField(parent,fixedSeed,loadFromFile); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"WaveletNoiseField::WaveletNoiseField" ); return 0; } catch(std::exception& e) { pbSetError("WaveletNoiseField::WaveletNoiseField",e.what()); return -1; } } + ~WaveletNoiseField() { + if(mNoiseTile) { delete mNoiseTile; mNoiseTile=NULL; } + }; + + //! evaluate noise + inline Real evaluate(Vec3 pos, int tile=0); + //! evaluate noise as a vector + inline Vec3 evaluateVec(Vec3 pos, int tile=0); + //! evaluate curl noise + inline Vec3 evaluateCurl(Vec3 pos); + + //! direct data access + Real* data() { return mNoiseTile; } + + //! compute wavelet decomposition of an input grid (stores residual coefficients) + static void computeCoefficients(Grid<Real>& input, Grid<Real>& tempIn1, Grid<Real>& tempIn2); + + // helper + std::string toString(); + + // texcoord position and scale + Vec3 mPosOffset;static PyObject* _GET_mPosOffset(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mPosOffset); } static int _SET_mPosOffset(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mPosOffset = fromPy<Vec3 >(val); return 0; } + Vec3 mPosScale;static PyObject* _GET_mPosScale(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mPosScale); } static int _SET_mPosScale(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mPosScale = fromPy<Vec3 >(val); return 0; } + // value offset & scale + Real mValOffset;static PyObject* _GET_mValOffset(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mValOffset); } static int _SET_mValOffset(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mValOffset = fromPy<Real >(val); return 0; } + Real mValScale;static PyObject* _GET_mValScale(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mValScale); } static int _SET_mValScale(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mValScale = fromPy<Real >(val); return 0; } + // clamp? (default 0-1) + bool mClamp;static PyObject* _GET_mClamp(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mClamp); } static int _SET_mClamp(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mClamp = fromPy<bool >(val); return 0; } + Real mClampNeg;static PyObject* _GET_mClampNeg(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mClampNeg); } static int _SET_mClampNeg(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mClampNeg = fromPy<Real >(val); return 0; } + Real mClampPos;static PyObject* _GET_mClampPos(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mClampPos); } static int _SET_mClampPos(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mClampPos = fromPy<Real >(val); return 0; } + // animated over time + Real mTimeAnim;static PyObject* _GET_mTimeAnim(PyObject* self, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); return toPy(pbo->mTimeAnim); } static int _SET_mTimeAnim(PyObject* self, PyObject* val, void* cl) { WaveletNoiseField* pbo = dynamic_cast<WaveletNoiseField*>(Pb::objFromPy(self)); pbo->mTimeAnim = fromPy<Real >(val); return 0; } + + protected: + // noise evaluation functions + static inline Real WNoiseDx (const Vec3& p, Real *data); + static inline Vec3 WNoiseVec(const Vec3& p, Real *data); + static inline Real WNoise (const Vec3& p, Real *data); + + // helpers for tile generation , for periodic 128 grids only + static void downsample(Real *from, Real *to, int n, int stride); + static void upsample (Real *from, Real *to, int n, int stride); + + // for grids with arbitrary sizes, and neumann boundary conditions + static void downsampleNeumann(const Real *from, Real *to, int n, int stride); + static void upsampleNeumann (const Real *from, Real *to, int n, int stride); + + static inline int modSlow(int x, int n) { int m = x % n; return (m<0) ? m+n : m; } + // warning - noiseTileSize has to be 128^3! + #define modFast128(x) ((x) & 127) + + inline Real getTime() { return mParent->getTime() * mParent->getDx() * mTimeAnim; } + + // pre-compute tile data for wavelet noise + void generateTile( int loadFromFile ); + + // animation over time + // grid size normalization (inverse size) + Real mGsInvX, mGsInvY, mGsInvZ; + // random offset into tile to simulate different random seeds + Vec3 mSeedOffset; + + static Real* mNoiseTile; // global random seed storage + static int randomSeed; public: PbArgs _args;} +#define _C_WaveletNoiseField +; + + + +// ************************************************************************** +// Implementation + +#define ADD_WEIGHTED(x,y,z)\ + weight = 1.0f;\ + xC = modFast128(midX + (x));\ + weight *= w[0][(x) + 1];\ + yC = modFast128(midY + (y));\ + weight *= w[1][(y) + 1];\ + zC = modFast128(midZ + (z));\ + weight *= w[2][(z) + 1];\ + result += weight * data[(zC * NOISE_TILE_SIZE + yC) * NOISE_TILE_SIZE + xC]; + +////////////////////////////////////////////////////////////////////////////////////////// +// derivatives of 3D noise - unrolled for performance +////////////////////////////////////////////////////////////////////////////////////////// +inline Real WaveletNoiseField::WNoiseDx(const Vec3& p, Real *data) { + Real w[3][3], t, result = 0; + + // Evaluate quadratic B-spline basis functions + int midX = (int)ceil(p[0] - 0.5f); + t = midX - (p[0] - 0.5f); + w[0][0] = -t; + w[0][2] = (1.f - t); + w[0][1] = 2.0f * t - 1.0f; + + int midY = (int)ceil(p[1] - 0.5f); + t = midY - (p[1] - 0.5f); + w[1][0] = t * t * 0.5f; + w[1][2] = (1.f - t) * (1.f - t) *0.5f; + w[1][1] = 1.f - w[1][0] - w[1][2]; + + int midZ = (int)ceil(p[2] - 0.5f); + t = midZ - (p[2] - 0.5f); + w[2][0] = t * t * 0.5f; + w[2][2] = (1.f - t) * (1.f - t) *0.5f; + w[2][1] = 1.f - w[2][0] - w[2][2]; + + // Evaluate noise by weighting noise coefficients by basis function values + int xC, yC, zC; + Real weight = 1; + + ADD_WEIGHTED(-1,-1, -1); ADD_WEIGHTED( 0,-1, -1); ADD_WEIGHTED( 1,-1, -1); + ADD_WEIGHTED(-1, 0, -1); ADD_WEIGHTED( 0, 0, -1); ADD_WEIGHTED( 1, 0, -1); + ADD_WEIGHTED(-1, 1, -1); ADD_WEIGHTED( 0, 1, -1); ADD_WEIGHTED( 1, 1, -1); + + ADD_WEIGHTED(-1,-1, 0); ADD_WEIGHTED( 0,-1, 0); ADD_WEIGHTED( 1,-1, 0); + ADD_WEIGHTED(-1, 0, 0); ADD_WEIGHTED( 0, 0, 0); ADD_WEIGHTED( 1, 0, 0); + ADD_WEIGHTED(-1, 1, 0); ADD_WEIGHTED( 0, 1, 0); ADD_WEIGHTED( 1, 1, 0); + + ADD_WEIGHTED(-1,-1, 1); ADD_WEIGHTED( 0,-1, 1); ADD_WEIGHTED( 1,-1, 1); + ADD_WEIGHTED(-1, 0, 1); ADD_WEIGHTED( 0, 0, 1); ADD_WEIGHTED( 1, 0, 1); + ADD_WEIGHTED(-1, 1, 1); ADD_WEIGHTED( 0, 1, 1); ADD_WEIGHTED( 1, 1, 1); + + return result; +} + +inline Real WaveletNoiseField::WNoise(const Vec3& p, Real *data) { + Real w[3][3], t, result = 0; + + // Evaluate quadratic B-spline basis functions + int midX = (int)ceilf(p[0] - 0.5f); + t = midX - (p[0] - 0.5f); + w[0][0] = t * t * 0.5f; + w[0][2] = (1.f - t) * (1.f - t) *0.5f; + w[0][1] = 1.f - w[0][0] - w[0][2]; + + int midY = (int)ceilf(p[1] - 0.5f); + t = midY - (p[1] - 0.5f); + w[1][0] = t * t * 0.5f; + w[1][2] = (1.f - t) * (1.f - t) *0.5f; + w[1][1] = 1.f - w[1][0] - w[1][2]; + + int midZ = (int)ceilf(p[2] - 0.5f); + t = midZ - (p[2] - 0.5f); + w[2][0] = t * t * 0.5f; + w[2][2] = (1.f - t) * (1.f - t) *0.5f; + w[2][1] = 1.f - w[2][0] - w[2][2]; + + // Evaluate noise by weighting noise coefficients by basis function values + int xC, yC, zC; + Real weight = 1; + + ADD_WEIGHTED(-1,-1, -1); ADD_WEIGHTED( 0,-1, -1); ADD_WEIGHTED( 1,-1, -1); + ADD_WEIGHTED(-1, 0, -1); ADD_WEIGHTED( 0, 0, -1); ADD_WEIGHTED( 1, 0, -1); + ADD_WEIGHTED(-1, 1, -1); ADD_WEIGHTED( 0, 1, -1); ADD_WEIGHTED( 1, 1, -1); + + ADD_WEIGHTED(-1,-1, 0); ADD_WEIGHTED( 0,-1, 0); ADD_WEIGHTED( 1,-1, 0); + ADD_WEIGHTED(-1, 0, 0); ADD_WEIGHTED( 0, 0, 0); ADD_WEIGHTED( 1, 0, 0); + ADD_WEIGHTED(-1, 1, 0); ADD_WEIGHTED( 0, 1, 0); ADD_WEIGHTED( 1, 1, 0); + + ADD_WEIGHTED(-1,-1, 1); ADD_WEIGHTED( 0,-1, 1); ADD_WEIGHTED( 1,-1, 1); + ADD_WEIGHTED(-1, 0, 1); ADD_WEIGHTED( 0, 0, 1); ADD_WEIGHTED( 1, 0, 1); + ADD_WEIGHTED(-1, 1, 1); ADD_WEIGHTED( 0, 1, 1); ADD_WEIGHTED( 1, 1, 1); + + return result; +} + + + +#define ADD_WEIGHTEDX(x,y,z)\ + weight = dw[0][(x) + 1] * w[1][(y) + 1] * w[2][(z) + 1];\ + result += weight * neighbors[x + 1][y + 1][z + 1]; + +#define ADD_WEIGHTEDY(x,y,z)\ + weight = w[0][(x) + 1] * dw[1][(y) + 1] * w[2][(z) + 1];\ + result += weight * neighbors[x + 1][y + 1][z + 1]; + +#define ADD_WEIGHTEDZ(x,y,z)\ + weight = w[0][(x) + 1] * w[1][(y) + 1] * dw[2][(z) + 1];\ + result += weight * neighbors[x + 1][y + 1][z + 1]; + +////////////////////////////////////////////////////////////////////////////////////////// +// compute all derivatives in at once +////////////////////////////////////////////////////////////////////////////////////////// +inline Vec3 WaveletNoiseField::WNoiseVec(const Vec3& p, Real *data) +{ + Vec3 final(0.); + Real w[3][3]; + Real dw[3][3]; + Real result = 0; + int xC, yC, zC; + Real weight; + + int midX = (int)ceil(p[0] - 0.5f); + int midY = (int)ceil(p[1] - 0.5f); + int midZ = (int)ceil(p[2] - 0.5f); + + Real t0 = midX - (p[0] - 0.5f); + Real t1 = midY - (p[1] - 0.5f); + Real t2 = midZ - (p[2] - 0.5f); + + // precache all the neighbors for fast access + Real neighbors[3][3][3]; + for (int z = -1; z <=1; z++) + for (int y = -1; y <= 1; y++) + for (int x = -1; x <= 1; x++) + { + xC = modFast128(midX + (x)); + yC = modFast128(midY + (y)); + zC = modFast128(midZ + (z)); + neighbors[x + 1][y + 1][z + 1] = data[zC * NOISE_TILE_SIZE * NOISE_TILE_SIZE + yC * NOISE_TILE_SIZE + xC]; + } + + /////////////////////////////////////////////////////////////////////////////////////// + // evaluate splines + /////////////////////////////////////////////////////////////////////////////////////// + dw[0][0] = -t0; + dw[0][2] = (1.f - t0); + dw[0][1] = 2.0f * t0 - 1.0f; + + dw[1][0] = -t1; + dw[1][2] = (1.0f - t1); + dw[1][1] = 2.0f * t1 - 1.0f; + + dw[2][0] = -t2; + dw[2][2] = (1.0f - t2); + dw[2][1] = 2.0f * t2 - 1.0f; + + w[0][0] = t0 * t0 * 0.5f; + w[0][2] = (1.f - t0) * (1.f - t0) *0.5f; + w[0][1] = 1.f - w[0][0] - w[0][2]; + + w[1][0] = t1 * t1 * 0.5f; + w[1][2] = (1.f - t1) * (1.f - t1) *0.5f; + w[1][1] = 1.f - w[1][0] - w[1][2]; + + w[2][0] = t2 * t2 * 0.5f; + w[2][2] = (1.f - t2) * (1.f - t2) *0.5f; + w[2][1] = 1.f - w[2][0] - w[2][2]; + + /////////////////////////////////////////////////////////////////////////////////////// + // x derivative + /////////////////////////////////////////////////////////////////////////////////////// + result = 0.0f; + ADD_WEIGHTEDX(-1,-1, -1); ADD_WEIGHTEDX( 0,-1, -1); ADD_WEIGHTEDX( 1,-1, -1); + ADD_WEIGHTEDX(-1, 0, -1); ADD_WEIGHTEDX( 0, 0, -1); ADD_WEIGHTEDX( 1, 0, -1); + ADD_WEIGHTEDX(-1, 1, -1); ADD_WEIGHTEDX( 0, 1, -1); ADD_WEIGHTEDX( 1, 1, -1); + + ADD_WEIGHTEDX(-1,-1, 0); ADD_WEIGHTEDX( 0,-1, 0); ADD_WEIGHTEDX( 1,-1, 0); + ADD_WEIGHTEDX(-1, 0, 0); ADD_WEIGHTEDX( 0, 0, 0); ADD_WEIGHTEDX( 1, 0, 0); + ADD_WEIGHTEDX(-1, 1, 0); ADD_WEIGHTEDX( 0, 1, 0); ADD_WEIGHTEDX( 1, 1, 0); + + ADD_WEIGHTEDX(-1,-1, 1); ADD_WEIGHTEDX( 0,-1, 1); ADD_WEIGHTEDX( 1,-1, 1); + ADD_WEIGHTEDX(-1, 0, 1); ADD_WEIGHTEDX( 0, 0, 1); ADD_WEIGHTEDX( 1, 0, 1); + ADD_WEIGHTEDX(-1, 1, 1); ADD_WEIGHTEDX( 0, 1, 1); ADD_WEIGHTEDX( 1, 1, 1); + final[0] = result; + + /////////////////////////////////////////////////////////////////////////////////////// + // y derivative + /////////////////////////////////////////////////////////////////////////////////////// + result = 0.0f; + ADD_WEIGHTEDY(-1,-1, -1); ADD_WEIGHTEDY( 0,-1, -1); ADD_WEIGHTEDY( 1,-1, -1); + ADD_WEIGHTEDY(-1, 0, -1); ADD_WEIGHTEDY( 0, 0, -1); ADD_WEIGHTEDY( 1, 0, -1); + ADD_WEIGHTEDY(-1, 1, -1); ADD_WEIGHTEDY( 0, 1, -1); ADD_WEIGHTEDY( 1, 1, -1); + + ADD_WEIGHTEDY(-1,-1, 0); ADD_WEIGHTEDY( 0,-1, 0); ADD_WEIGHTEDY( 1,-1, 0); + ADD_WEIGHTEDY(-1, 0, 0); ADD_WEIGHTEDY( 0, 0, 0); ADD_WEIGHTEDY( 1, 0, 0); + ADD_WEIGHTEDY(-1, 1, 0); ADD_WEIGHTEDY( 0, 1, 0); ADD_WEIGHTEDY( 1, 1, 0); + + ADD_WEIGHTEDY(-1,-1, 1); ADD_WEIGHTEDY( 0,-1, 1); ADD_WEIGHTEDY( 1,-1, 1); + ADD_WEIGHTEDY(-1, 0, 1); ADD_WEIGHTEDY( 0, 0, 1); ADD_WEIGHTEDY( 1, 0, 1); + ADD_WEIGHTEDY(-1, 1, 1); ADD_WEIGHTEDY( 0, 1, 1); ADD_WEIGHTEDY( 1, 1, 1); + final[1] = result; + + /////////////////////////////////////////////////////////////////////////////////////// + // z derivative + /////////////////////////////////////////////////////////////////////////////////////// + result = 0.0f; + ADD_WEIGHTEDZ(-1,-1, -1); ADD_WEIGHTEDZ( 0,-1, -1); ADD_WEIGHTEDZ( 1,-1, -1); + ADD_WEIGHTEDZ(-1, 0, -1); ADD_WEIGHTEDZ( 0, 0, -1); ADD_WEIGHTEDZ( 1, 0, -1); + ADD_WEIGHTEDZ(-1, 1, -1); ADD_WEIGHTEDZ( 0, 1, -1); ADD_WEIGHTEDZ( 1, 1, -1); + + ADD_WEIGHTEDZ(-1,-1, 0); ADD_WEIGHTEDZ( 0,-1, 0); ADD_WEIGHTEDZ( 1,-1, 0); + ADD_WEIGHTEDZ(-1, 0, 0); ADD_WEIGHTEDZ( 0, 0, 0); ADD_WEIGHTEDZ( 1, 0, 0); + ADD_WEIGHTEDZ(-1, 1, 0); ADD_WEIGHTEDZ( 0, 1, 0); ADD_WEIGHTEDZ( 1, 1, 0); + + ADD_WEIGHTEDZ(-1,-1, 1); ADD_WEIGHTEDZ( 0,-1, 1); ADD_WEIGHTEDZ( 1,-1, 1); + ADD_WEIGHTEDZ(-1, 0, 1); ADD_WEIGHTEDZ( 0, 0, 1); ADD_WEIGHTEDZ( 1, 0, 1); + ADD_WEIGHTEDZ(-1, 1, 1); ADD_WEIGHTEDZ( 0, 1, 1); ADD_WEIGHTEDZ( 1, 1, 1); + final[2] = result; + + //debMsg("FINAL","at "<<p<<" = "<<final); // DEBUG + return final; +} +#undef ADD_WEIGHTEDX +#undef ADD_WEIGHTEDY +#undef ADD_WEIGHTEDZ + +inline Real WaveletNoiseField::evaluate(Vec3 pos, int tile) { + pos[0] *= mGsInvX; + pos[1] *= mGsInvY; + pos[2] *= mGsInvZ; + pos += mSeedOffset; + + // time anim + pos += Vec3(getTime()); + + pos[0] *= mPosScale[0]; + pos[1] *= mPosScale[1]; + pos[2] *= mPosScale[2]; + pos += mPosOffset; + + const int n3 = square(NOISE_TILE_SIZE) * NOISE_TILE_SIZE; + Real v = WNoise(pos, &mNoiseTile[tile*n3]); + + v += mValOffset; + v *= mValScale; + if (mClamp) { + if (v< mClampNeg) v = mClampNeg; + if (v> mClampPos) v = mClampPos; + } + return v; +} + +inline Vec3 WaveletNoiseField::evaluateVec(Vec3 pos, int tile) { + pos[0] *= mGsInvX; + pos[1] *= mGsInvY; + pos[2] *= mGsInvZ; + pos += mSeedOffset; + + // time anim + pos += Vec3(getTime()); + + pos[0] *= mPosScale[0]; + pos[1] *= mPosScale[1]; + pos[2] *= mPosScale[2]; + pos += mPosOffset; + + const int n3 = square(NOISE_TILE_SIZE) * NOISE_TILE_SIZE; + Vec3 v = WNoiseVec(pos, &mNoiseTile[tile*n3]); + + v += Vec3(mValOffset); + v *= mValScale; + + if (mClamp) { + for(int i=0; i<3; i++) { + if (v[i]< mClampNeg) v[i] = mClampNeg; + if (v[i]> mClampPos) v[i] = mClampPos; + } + } + return v; +} + +inline Vec3 WaveletNoiseField::evaluateCurl(Vec3 pos) { + // gradients of w0-w2 + Vec3 d0 = evaluateVec(pos,0), + d1 = evaluateVec(pos,1), + d2 = evaluateVec(pos,2); + + return Vec3(d0.y-d1.z, d2.z-d0.x, d1.x-d2.y); +} + +} // namespace + +#endif + + diff --git a/source/blender/python/manta_pp/noisefield.h.reg b/source/blender/python/manta_pp/noisefield.h.reg new file mode 100644 index 00000000000..74b1bd96478 --- /dev/null +++ b/source/blender/python/manta_pp/noisefield.h.reg @@ -0,0 +1,12 @@ +#include "noisefield.h" ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","NoiseField","PbClass"); template<> const char* Namify<WaveletNoiseField >::S = "WaveletNoiseField"; +>WaveletNoiseField^ ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","WaveletNoiseField",WaveletNoiseField::_W_0); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","posOffset",WaveletNoiseField::_GET_mPosOffset,WaveletNoiseField::_SET_mPosOffset); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","posScale",WaveletNoiseField::_GET_mPosScale,WaveletNoiseField::_SET_mPosScale); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","valOffset",WaveletNoiseField::_GET_mValOffset,WaveletNoiseField::_SET_mValOffset); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","valScale",WaveletNoiseField::_GET_mValScale,WaveletNoiseField::_SET_mValScale); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","clamp",WaveletNoiseField::_GET_mClamp,WaveletNoiseField::_SET_mClamp); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","clampNeg",WaveletNoiseField::_GET_mClampNeg,WaveletNoiseField::_SET_mClampNeg); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","clampPos",WaveletNoiseField::_GET_mClampPos,WaveletNoiseField::_SET_mClampPos); ++WaveletNoiseField^ static const Pb::Register _R_$IDX$ ("WaveletNoiseField","timeAnim",WaveletNoiseField::_GET_mTimeAnim,WaveletNoiseField::_SET_mTimeAnim); diff --git a/source/blender/python/manta_pp/noisefield.h.reg.cpp b/source/blender/python/manta_pp/noisefield.h.reg.cpp new file mode 100644 index 00000000000..967b3b2cdd1 --- /dev/null +++ b/source/blender/python/manta_pp/noisefield.h.reg.cpp @@ -0,0 +1,26 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "noisefield.h" +namespace Manta { +#ifdef _C_WaveletNoiseField + static const Pb::Register _R_0 ("WaveletNoiseField","NoiseField","PbClass"); template<> const char* Namify<WaveletNoiseField >::S = "WaveletNoiseField"; + static const Pb::Register _R_1 ("WaveletNoiseField","WaveletNoiseField",WaveletNoiseField::_W_0); + static const Pb::Register _R_2 ("WaveletNoiseField","posOffset",WaveletNoiseField::_GET_mPosOffset,WaveletNoiseField::_SET_mPosOffset); + static const Pb::Register _R_3 ("WaveletNoiseField","posScale",WaveletNoiseField::_GET_mPosScale,WaveletNoiseField::_SET_mPosScale); + static const Pb::Register _R_4 ("WaveletNoiseField","valOffset",WaveletNoiseField::_GET_mValOffset,WaveletNoiseField::_SET_mValOffset); + static const Pb::Register _R_5 ("WaveletNoiseField","valScale",WaveletNoiseField::_GET_mValScale,WaveletNoiseField::_SET_mValScale); + static const Pb::Register _R_6 ("WaveletNoiseField","clamp",WaveletNoiseField::_GET_mClamp,WaveletNoiseField::_SET_mClamp); + static const Pb::Register _R_7 ("WaveletNoiseField","clampNeg",WaveletNoiseField::_GET_mClampNeg,WaveletNoiseField::_SET_mClampNeg); + static const Pb::Register _R_8 ("WaveletNoiseField","clampPos",WaveletNoiseField::_GET_mClampPos,WaveletNoiseField::_SET_mClampPos); + static const Pb::Register _R_9 ("WaveletNoiseField","timeAnim",WaveletNoiseField::_GET_mTimeAnim,WaveletNoiseField::_SET_mTimeAnim); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/particle.cpp b/source/blender/python/manta_pp/particle.cpp new file mode 100644 index 00000000000..edeb9c105a3 --- /dev/null +++ b/source/blender/python/manta_pp/particle.cpp @@ -0,0 +1,519 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2013 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Particle data functionality + * + ******************************************************************************/ + +#include <fstream> +#include <cstring> +#include <limits> +#if NO_ZLIB!=1 +#include <zlib.h> +#endif +#include "particle.h" +#include "levelset.h" +#include "fileio.h" + +using namespace std; +namespace Manta { + + +ParticleBase::ParticleBase(FluidSolver* parent) : + PbClass(parent), mAllowCompress(true), mFreePdata(false) { +} + +ParticleBase::~ParticleBase() +{ + // make sure data fields now parent system is deleted + for(int i=0; i<(int)mPartData.size(); ++i) + mPartData[i]->setParticleSys(NULL); + + if(mFreePdata) { + for(int i=0; i<(int)mPartData.size(); ++i) + delete mPartData[i]; + } + +} + +std::string ParticleBase::infoString() const { + return "ParticleSystem " + mName + " <no info>"; +} + +void ParticleBase::cloneParticleData(ParticleBase* nm) { + // clone additional data , and make sure the copied particle system deletes it + nm->mFreePdata = true; + for(int i=0; i<(int)mPartData.size(); ++i) { + ParticleDataBase* pdata = mPartData[i]->clone(); + nm->registerPdata(pdata); + } +} + +void ParticleBase::deregister(ParticleDataBase* pdata) { + bool done = false; + // remove pointer from particle data list + for(int i=0; i<(int)mPartData.size(); ++i) { + if(mPartData[i] == pdata) { + if(i<(int)mPartData.size()-1) + mPartData[i] = mPartData[mPartData.size()-1]; + mPartData.pop_back(); + done = true; + } + } + if(!done) + errMsg("Invalid pointer given, not registered!"); +} + +// create and attach a new pdata field to this particle system +PbClass* ParticleBase::create(PbType t, PbTypeVec T, const string& name) { + _args.add("nocheck",true); + if (t.str() == "") + errMsg("Specify particle data type to create"); + //debMsg( "Pdata creating '"<< t.str <<" with size "<< this->getSizeSlow(), 5 ); + + PbClass* pyObj = PbClass::createPyObject(t.str() + T.str(), name, _args, this->getParent() ); + + ParticleDataBase* pdata = dynamic_cast<ParticleDataBase*>(pyObj); + if(!pdata) { + errMsg("Unable to get particle data pointer from newly created object. Only create ParticleData type with a ParticleSys.creat() call, eg, PdataReal, PdataVec3 etc."); + delete pyObj; + return NULL; + } else { + this->registerPdata(pdata); + } + + // directly init size of new pdata field: + pdata->resize( this->getSizeSlow() ); + return pyObj; +} + +void ParticleBase::registerPdata(ParticleDataBase* pdata) { + pdata->setParticleSys(this); + mPartData.push_back(pdata); + + if( pdata->getType() == ParticleDataBase::TypeReal ) { + ParticleDataImpl<Real>* pd = dynamic_cast< ParticleDataImpl<Real>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as real!"); + this->registerPdataReal(pd); + } + else if( pdata->getType() == ParticleDataBase::TypeInt ) { + ParticleDataImpl<int>* pd = dynamic_cast< ParticleDataImpl<int>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as int!"); + this->registerPdataInt(pd); + } + else if( pdata->getType() == ParticleDataBase::TypeVec3 ) { + ParticleDataImpl<Vec3>* pd = dynamic_cast< ParticleDataImpl<Vec3>* >(pdata); + if(!pd) errMsg("Invalid pdata object posing as vec3!"); + this->registerPdataVec3(pd); + } +} +void ParticleBase::registerPdataReal(ParticleDataImpl<Real>* pd) { mPdataReal.push_back(pd); } +void ParticleBase::registerPdataVec3(ParticleDataImpl<Vec3>* pd) { mPdataVec3.push_back(pd); } +void ParticleBase::registerPdataInt (ParticleDataImpl<int >* pd) { mPdataInt .push_back(pd); } + +void ParticleBase::addAllPdata() { + for(int i=0; i<(int)mPartData.size(); ++i) { + mPartData[i]->addEntry(); + } +} + + +BasicParticleSystem::BasicParticleSystem(FluidSolver* parent) + : ParticleSystem<BasicParticleData>(parent) { + this->mAllowCompress = false; +} + +// file io + +void BasicParticleSystem::writeParticlesText(string name) { + ofstream ofs(name.c_str()); + if (!ofs.good()) + errMsg("can't open file!"); + ofs << this->size()<<", pdata: "<< mPartData.size()<<" ("<<mPdataInt.size()<<","<<mPdataReal.size()<<","<<mPdataVec3.size()<<") \n"; + for(int i=0; i<this->size(); ++i) { + ofs << i<<": "<< this->getPos(i) <<" , "<< this->getStatus(i) <<". "; + for(int pd=0; pd<(int)mPdataInt.size() ; ++pd) ofs << mPdataInt [pd]->get(i)<<" "; + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) ofs << mPdataReal[pd]->get(i)<<" "; + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) ofs << mPdataVec3[pd]->get(i)<<" "; + ofs << "\n"; + } + ofs.close(); +} + +void BasicParticleSystem::writeParticlesRawPositionsGz(string name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); + if (!gzf) errMsg("can't open file "<<name); + for(int i=0; i<this->size(); ++i) { + Vector3D<float> p = toVec3f( this->getPos(i) ); + gzwrite(gzf, &p, sizeof(float)*3); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + +void BasicParticleSystem::writeParticlesRawVelocityGz(string name) { +# if NO_ZLIB!=1 + gzFile gzf = gzopen(name.c_str(), "wb1"); + if (!gzf) errMsg("can't open file "<<name); + if( mPdataVec3.size() < 1 ) errMsg("no vec3 particle data channel found!"); + // note , assuming particle data vec3 0 is velocity! make optional... + for(int i=0; i<this->size(); ++i) { + Vector3D<float> p = toVec3f( mPdataVec3[0]->get(i) ); + gzwrite(gzf, &p, sizeof(float)*3); + } + gzclose(gzf); +# else + cout << "file format not supported without zlib" << endl; +# endif +} + + +void BasicParticleSystem::load(string name ) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if ( ext == ".uni") + readParticlesUni<BasicParticleData>(name, this ); + else + errMsg("particle '" + name +"' filetype not supported for loading"); +} + +void BasicParticleSystem::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".txt") + this->writeParticlesText(name); + else if (ext == ".uni") + writeParticlesUni<BasicParticleData>(name, this); + // raw data formats, very basic for simple data transfer to other programs + else if (ext == ".posgz") + this->writeParticlesRawPositionsGz(name); + else if (ext == ".velgz") + this->writeParticlesRawVelocityGz(name); + else + errMsg("particle '" + name +"' filetype not supported for saving"); +} + +void BasicParticleSystem::printParts(int start, int stop, bool printIndex) +{ + std::ostringstream sstr; + int s = (start>0 ? start : 0 ); + int e = (stop>0 ? stop : (int)mData.size() ); + s = Manta::clamp(s, 0, (int)mData.size()); + e = Manta::clamp(e, 0, (int)mData.size()); + + for(int i=s; i<e; ++i) { + if(printIndex) sstr << i<<": "; + sstr<<mData[i].pos<<" "<<mData[i].flag<<"\n"; + } + debMsg( sstr.str() , 1 ); +} + +// particle data + +ParticleDataBase::ParticleDataBase(FluidSolver* parent) : + PbClass(parent) , mpParticleSys(NULL) { +} + +ParticleDataBase::~ParticleDataBase() +{ + // notify parent of deletion + if(mpParticleSys) + mpParticleSys->deregister(this); +} + + +// actual data implementation + +template<class T> +ParticleDataImpl<T>::ParticleDataImpl(FluidSolver* parent) : + ParticleDataBase(parent) , mpGridSource(NULL), mGridSourceMAC(false) { +} + +template<class T> +ParticleDataImpl<T>::ParticleDataImpl(FluidSolver* parent, ParticleDataImpl<T>* other) : + ParticleDataBase(parent) , mpGridSource(NULL), mGridSourceMAC(false) { + this->mData = other->mData; +} + +template<class T> +ParticleDataImpl<T>::~ParticleDataImpl() { +} + +template<class T> +int ParticleDataImpl<T>::getSizeSlow() const { + return mData.size(); +} +template<class T> +void ParticleDataImpl<T>::addEntry() { + // add zero'ed entry + T tmp = T(0.); + // for debugging, force init: + //tmp = T(0.02 * mData.size()); // increasing + //tmp = T(1.); // constant 1 + return mData.push_back(tmp); +} +template<class T> +void ParticleDataImpl<T>::resize(int s) { + mData.resize(s); +} +template<class T> +void ParticleDataImpl<T>::copyValueSlow(int from, int to) { + this->copyValue(from,to); +} +template<class T> +ParticleDataBase* ParticleDataImpl<T>::clone() { + ParticleDataImpl<T>* npd = new ParticleDataImpl<T>( getParent(), this ); + return npd; +} + +template<class T> +void ParticleDataImpl<T>::setSource(Grid<T>* grid, bool isMAC ) { + mpGridSource = grid; + mGridSourceMAC = isMAC; + if(isMAC) assertMsg( dynamic_cast<MACGrid*>(grid) != NULL , "Given grid is not a valid MAC grid"); +} + +template<class T> +void ParticleDataImpl<T>::initNewValue(int idx, Vec3 pos) { + if(!mpGridSource) + mData[idx] = 0; + else { + mData[idx] = mpGridSource->getInterpolated(pos); + } +} +// special handling needed for velocities +template<> +void ParticleDataImpl<Vec3>::initNewValue(int idx, Vec3 pos) { + if(!mpGridSource) + mData[idx] = 0; + else { + if(!mGridSourceMAC) + mData[idx] = mpGridSource->getInterpolated(pos); + else + mData[idx] = ((MACGrid*)mpGridSource)->getInterpolated(pos); + } +} + +template<typename T> +void ParticleDataImpl<T>::load(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if ( ext == ".uni") + readPdataUni<T>(name, this); + else + errMsg("particle data '" + name +"' filetype not supported for loading"); +} + +template<typename T> +void ParticleDataImpl<T>::save(string name) { + if (name.find_last_of('.') == string::npos) + errMsg("file '" + name + "' does not have an extension"); + string ext = name.substr(name.find_last_of('.')); + if (ext == ".uni") + writePdataUni<T>(name, this); + else + errMsg("particle data '" + name +"' filetype not supported for saving"); +} + +// specializations + +template<> +ParticleDataBase::PdataType ParticleDataImpl<Real>::getType() const { + return ParticleDataBase::TypeReal; +} +template<> +ParticleDataBase::PdataType ParticleDataImpl<int>::getType() const { + return ParticleDataBase::TypeInt; +} +template<> +ParticleDataBase::PdataType ParticleDataImpl<Vec3>::getType() const { + return ParticleDataBase::TypeVec3; +} + +// note, we need a flag value for functions such as advection +// ideally, this value should never be modified +int ParticleIndexData::flag = 0; +Vec3 ParticleIndexData::pos = Vec3(0.,0.,0.); + +template <class T> struct knSetPdataConst : public KernelBase { knSetPdataConst(ParticleDataImpl<T>& pdata, T value) : KernelBase(pdata.size()) ,pdata(pdata),value(value) { run(); } inline void op(int idx, ParticleDataImpl<T>& pdata, T value ) { pdata[idx] = value; } inline ParticleDataImpl<T>& getArg0() { return pdata; } typedef ParticleDataImpl<T> type0;inline T& getArg1() { return value; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, pdata,value); } ParticleDataImpl<T>& pdata; T value; }; + +template <class T, class S> struct knPdataSet : public KernelBase { knPdataSet(ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other ) { me[idx] += other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<S>& getArg1() { return other; } typedef ParticleDataImpl<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<S>& other; }; +template <class T, class S> struct knPdataAdd : public KernelBase { knPdataAdd(ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other ) { me[idx] += other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<S>& getArg1() { return other; } typedef ParticleDataImpl<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<S>& other; }; +template <class T, class S> struct knPdataSub : public KernelBase { knPdataSub(ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other ) { me[idx] -= other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<S>& getArg1() { return other; } typedef ParticleDataImpl<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<S>& other; }; +template <class T, class S> struct knPdataMult : public KernelBase { knPdataMult(ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other ) { me[idx] *= other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<S>& getArg1() { return other; } typedef ParticleDataImpl<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<S>& other; }; +template <class T, class S> struct knPdataDiv : public KernelBase { knPdataDiv(ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<S>& other ) { me[idx] /= other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<S>& getArg1() { return other; } typedef ParticleDataImpl<S> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<S>& other; }; + +template <class T, class S> struct knPdataSetScalar : public KernelBase { knPdataSetScalar(ParticleDataImpl<T>& me, const S& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const S& other ) { me[idx] = other; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const S& getArg1() { return other; } typedef S type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const S& other; }; +template <class T, class S> struct knPdataAddScalar : public KernelBase { knPdataAddScalar(ParticleDataImpl<T>& me, const S& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const S& other ) { me[idx] += other; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const S& getArg1() { return other; } typedef S type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const S& other; }; +template <class T, class S> struct knPdataMultScalar : public KernelBase { knPdataMultScalar(ParticleDataImpl<T>& me, const S& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const S& other ) { me[idx] *= other; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const S& getArg1() { return other; } typedef S type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const S& other; }; +template <class T, class S> struct knPdataScaledAdd : public KernelBase { knPdataScaledAdd(ParticleDataImpl<T>& me, const ParticleDataImpl<T>& other, const S& factor) : KernelBase(me.size()) ,me(me),other(other),factor(factor) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<T>& other, const S& factor ) { me[idx] += factor * other[idx]; } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<T>& getArg1() { return other; } typedef ParticleDataImpl<T> type1;inline const S& getArg2() { return factor; } typedef S type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other,factor); } ParticleDataImpl<T>& me; const ParticleDataImpl<T>& other; const S& factor; }; + +template <class T> struct knPdataSafeDiv : public KernelBase { knPdataSafeDiv(ParticleDataImpl<T>& me, const ParticleDataImpl<T>& other) : KernelBase(me.size()) ,me(me),other(other) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, const ParticleDataImpl<T>& other ) { me[idx] = safeDivide(me[idx], other[idx]); } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline const ParticleDataImpl<T>& getArg1() { return other; } typedef ParticleDataImpl<T> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other); } ParticleDataImpl<T>& me; const ParticleDataImpl<T>& other; }; +template <class T> struct knPdataSetConst : public KernelBase { knPdataSetConst(ParticleDataImpl<T>& pdata, T value) : KernelBase(pdata.size()) ,pdata(pdata),value(value) { run(); } inline void op(int idx, ParticleDataImpl<T>& pdata, T value ) { pdata[idx] = value; } inline ParticleDataImpl<T>& getArg0() { return pdata; } typedef ParticleDataImpl<T> type0;inline T& getArg1() { return value; } typedef T type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, pdata,value); } ParticleDataImpl<T>& pdata; T value; }; + +template <class T> struct knPdataClamp : public KernelBase { knPdataClamp(ParticleDataImpl<T>& me, T min, T max) : KernelBase(me.size()) ,me(me),min(min),max(max) { run(); } inline void op(int idx, ParticleDataImpl<T>& me, T min, T max ) { me[idx] = clamp( me[idx], min, max); } inline ParticleDataImpl<T>& getArg0() { return me; } typedef ParticleDataImpl<T> type0;inline T& getArg1() { return min; } typedef T type1;inline T& getArg2() { return max; } typedef T type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,min,max); } ParticleDataImpl<T>& me; T min; T max; }; + +// python operators + + +template<typename T> +ParticleDataImpl<T>& ParticleDataImpl<T>::copyFrom(const ParticleDataImpl<T>& a) { + assertMsg (a.mData.size() == mData.size() , "different pdata size "<<a.mData.size()<<" vs "<<this->mData.size() ); + memcpy( &mData[0], &a.mData[0], sizeof(T) * mData.size() ); + return *this; +} + +template<typename T> +void ParticleDataImpl<T>::setConst(T s) { + knPdataSetScalar<T,T> op( *this, s ); +} + +template<typename T> +void ParticleDataImpl<T>::add(const ParticleDataImpl<T>& a) { + knPdataAdd<T,T> op( *this, a ); +} +template<typename T> +void ParticleDataImpl<T>::sub(const ParticleDataImpl<T>& a) { + knPdataSub<T,T> op( *this, a ); +} + +template<typename T> +void ParticleDataImpl<T>::addConst(T s) { + knPdataAddScalar<T,T> op( *this, s ); +} + +template<typename T> +void ParticleDataImpl<T>::addScaled(const ParticleDataImpl<T>& a, const T& factor) { + knPdataScaledAdd<T,T> op( *this, a, factor ); +} + +template<typename T> +void ParticleDataImpl<T>::mult( const ParticleDataImpl<T>& a) { + knPdataMult<T,T> op( *this, a ); +} + +template<typename T> +void ParticleDataImpl<T>::multConst(T s) { + knPdataMultScalar<T,T> op( *this, s ); +} + +template<typename T> +void ParticleDataImpl<T>::clamp(Real min, Real max) { + knPdataClamp<T> op( *this, min,max ); +} + +template<typename T> + + struct CompPdata_Min : public KernelBase { CompPdata_Min(const ParticleDataImpl<T>& val) : KernelBase(val.size()) ,val(val) ,minVal(std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, const ParticleDataImpl<T>& val ,Real& minVal) { + if (val[idx] < minVal) + minVal = val[idx]; +} inline operator Real () { return minVal; } inline Real & getRet() { return minVal; } inline const ParticleDataImpl<T>& getArg0() { return val; } typedef ParticleDataImpl<T> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,minVal); } const ParticleDataImpl<T>& val; Real minVal; }; + +template<typename T> + + struct CompPdata_Max : public KernelBase { CompPdata_Max(const ParticleDataImpl<T>& val) : KernelBase(val.size()) ,val(val) ,maxVal(-std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, const ParticleDataImpl<T>& val ,Real& maxVal) { + if (val[idx] > maxVal) + maxVal = val[idx]; +} inline operator Real () { return maxVal; } inline Real & getRet() { return maxVal; } inline const ParticleDataImpl<T>& getArg0() { return val; } typedef ParticleDataImpl<T> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,maxVal); } const ParticleDataImpl<T>& val; Real maxVal; }; + +template<typename T> +Real ParticleDataImpl<T>::getMinValue() { + return sqrt(CompPdata_Min<T> (*this)); +} + +template<typename T> +Real ParticleDataImpl<T>::getMaxAbsValue() { + Real amin = CompPdata_Min<T> (*this); + Real amax = CompPdata_Max<T> (*this); + return max( fabs(amin), fabs(amax)); +} + +template<typename T> +Real ParticleDataImpl<T>::getMaxValue() { + return sqrt(CompPdata_Max<T> (*this)); +} + +template<typename T> +void ParticleDataImpl<T>::printPdata(int start, int stop, bool printIndex) +{ + std::ostringstream sstr; + int s = (start>0 ? start : 0 ); + int e = (stop>0 ? stop : (int)mData.size() ); + s = Manta::clamp(s, 0, (int)mData.size()); + e = Manta::clamp(e, 0, (int)mData.size()); + + for(int i=s; i<e; ++i) { + if(printIndex) sstr << i<<": "; + sstr<<mData[i]<<" "<<"\n"; + } + debMsg( sstr.str() , 1 ); +} + +// specials for vec3 + + + + struct CompPdata_MinVec3 : public KernelBase { CompPdata_MinVec3(const ParticleDataImpl<Vec3>& val) : KernelBase(val.size()) ,val(val) ,minVal(-std::numeric_limits<Real>::max()) { run(); } inline void op(int idx, const ParticleDataImpl<Vec3>& val ,Real& minVal) { + const Real s = normSquare(val[idx]); + if (s < minVal) + minVal = s; +} inline operator Real () { return minVal; } inline Real & getRet() { return minVal; } inline const ParticleDataImpl<Vec3>& getArg0() { return val; } typedef ParticleDataImpl<Vec3> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,minVal); } const ParticleDataImpl<Vec3>& val; Real minVal; }; + + + struct CompPdata_MaxVec3 : public KernelBase { CompPdata_MaxVec3(const ParticleDataImpl<Vec3>& val) : KernelBase(val.size()) ,val(val) ,maxVal(-std::numeric_limits<Real>::min()) { run(); } inline void op(int idx, const ParticleDataImpl<Vec3>& val ,Real& maxVal) { + const Real s = normSquare(val[idx]); + if (s > maxVal) + maxVal = s; +} inline operator Real () { return maxVal; } inline Real & getRet() { return maxVal; } inline const ParticleDataImpl<Vec3>& getArg0() { return val; } typedef ParticleDataImpl<Vec3> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, val,maxVal); } const ParticleDataImpl<Vec3>& val; Real maxVal; }; + +template<> +Real ParticleDataImpl<Vec3>::getMinValue() { + return sqrt(CompPdata_MinVec3 (*this)); +} + +template<> +Real ParticleDataImpl<Vec3>::getMaxAbsValue() { + Real amin = CompPdata_MinVec3 (*this); + Real amax = CompPdata_MaxVec3 (*this); + return max( fabs(amin), fabs(amax)); +} + +template<> +Real ParticleDataImpl<Vec3>::getMaxValue() { + return sqrt(CompPdata_MaxVec3 (*this)); +} + + +// explicit instantiation +template class ParticleDataImpl<int>; +template class ParticleDataImpl<Real>; +template class ParticleDataImpl<Vec3>; + + +} // namespace + + + diff --git a/source/blender/python/manta_pp/particle.h b/source/blender/python/manta_pp/particle.h new file mode 100644 index 00000000000..8d2341da47a --- /dev/null +++ b/source/blender/python/manta_pp/particle.h @@ -0,0 +1,665 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for particle systems + * + ******************************************************************************/ + +#ifndef _PARTICLE_H +#define _PARTICLE_H + +#include <vector> +#include "grid.h" +#include "vectorbase.h" +#include "integrator.h" +#include "randomstream.h" +namespace Manta { + +// fwd decl +template<class T> class Grid; +class ParticleDataBase; +template<class T> class ParticleDataImpl; + +//! Baseclass for particle systems. Does not implement any data +class ParticleBase : public PbClass {public: + enum SystemType { BASE=0, PARTICLE, VORTEX, FILAMENT, FLIP, TURBULENCE, INDEX }; + + enum ParticleStatus { + PNONE = 0, + PNEW = (1<<1), // particles newly created in this step + PDELETE = (1<<10), // mark as deleted, will be deleted in next compress() step + PINVALID = (1<<30), // unused + }; + + ParticleBase(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ParticleBase::ParticleBase" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ParticleBase(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ParticleBase::ParticleBase" ); return 0; } catch(std::exception& e) { pbSetError("ParticleBase::ParticleBase",e.what()); return -1; } } + virtual ~ParticleBase(); + + //! copy all the particle data thats registered with the other particle system to this one + virtual void cloneParticleData(ParticleBase* nm); + + virtual SystemType getType() const { return BASE; } + virtual std::string infoString() const; + virtual ParticleBase* clone() { assertMsg( false , "Dont use, override..."); return NULL; } + + // slow virtual function to query size, do not use in kernels! use size() instead + virtual int getSizeSlow() const { assertMsg( false , "Dont use, override..."); return 0; } + + //! add a position as potential candidate for new particle (todo, make usable from parallel threads) + inline void addBuffered(const Vec3& pos); + + // particle data functions + + //! create a particle data object + PbClass* create(PbType type, PbTypeVec T=PbTypeVec(), const std::string& name = ""); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleBase* pbo = dynamic_cast<ParticleBase*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleBase::create"); PyObject *_retval = 0; { ArgLocker _lock; PbType type = _args.get<PbType >("type",0,&_lock); PbTypeVec T = _args.getOpt<PbTypeVec >("T",1,PbTypeVec(),&_lock); const std::string& name = _args.getOpt<std::string >("name",2,"",&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->create(type,T,name)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleBase::create"); return _retval; } catch(std::exception& e) { pbSetError("ParticleBase::create",e.what()); return 0; } } + //! add a particle data field, set its parent particle-system pointer + void registerPdata(ParticleDataBase* pdata); + void registerPdataReal(ParticleDataImpl<Real>* pdata); + void registerPdataVec3(ParticleDataImpl<Vec3>* pdata); + void registerPdataInt (ParticleDataImpl<int >* pdata); + //! remove a particle data entry + void deregister(ParticleDataBase* pdata); + //! add one zero entry to all data fields + void addAllPdata(); + // note - deletion of pdata is handled in compress function + + //! how many are there? + int getNumPdata() const { return mPartData.size(); } + //! access one of the fields + ParticleDataBase* getPdata(int i) { return mPartData[i]; } + +protected: + //! new particle candidates + std::vector<Vec3> mNewBuffer; + + //! allow automatic compression / resize? disallowed for, eg, flip particle systems + bool mAllowCompress; + + //! store particle data , each pointer has its own storage vector of a certain type (int, real, vec3) + std::vector<ParticleDataBase*> mPartData; + //! lists of different types, for fast operations w/o virtual function calls (all calls necessary per particle) + std::vector< ParticleDataImpl<Real> *> mPdataReal; + std::vector< ParticleDataImpl<Vec3> *> mPdataVec3; + std::vector< ParticleDataImpl<int> *> mPdataInt; //! indicate that pdata of this particle system is copied, and needs to be freed + bool mFreePdata; public: PbArgs _args;} +#define _C_ParticleBase +; + + +//! Main class for particle systems +/*! Basetype S must at least contain flag, pos fields */ +template<class S> class ParticleSystem : public ParticleBase {public: + ParticleSystem(FluidSolver* parent) :ParticleBase(parent),mDeletes(0),mDeleteChunk(0){} static int _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ParticleSystem::ParticleSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ParticleSystem(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ParticleSystem::ParticleSystem" ); return 0; } catch(std::exception& e) { pbSetError("ParticleSystem::ParticleSystem",e.what()); return -1; } } + virtual ~ParticleSystem() {}; + + virtual SystemType getType() const { return S::getType(); }; + + // accessors + inline S& operator[](int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const S& operator[](int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + // return size of container + inline int size() const { return mData.size(); } static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::size"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->size()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::size"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::size",e.what()); return 0; } } + // slow virtual function of base class, also returns size + virtual int getSizeSlow() const { return size(); } + + // query status + inline int getStatus(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx].flag; } + inline bool isActive(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PDELETE) == 0; } + + //! safe accessor for python + void setPos(int idx, const Vec3& pos) { DEBUG_ONLY(checkPartIndex(idx)); mData[idx].pos = pos; } static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::setPos"); PyObject *_retval = 0; { ArgLocker _lock; int idx = _args.get<int >("idx",0,&_lock); const Vec3& pos = _args.get<Vec3 >("pos",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setPos(idx,pos); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::setPos"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::setPos",e.what()); return 0; } } + Vec3 getPos(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx].pos; } static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::getPos"); PyObject *_retval = 0; { ArgLocker _lock; int idx = _args.get<int >("idx",0,&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->getPos(idx)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::getPos"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::getPos",e.what()); return 0; } } + //! copy all positions into pdata vec3 field + void getPosPdata(ParticleDataImpl<Vec3>& target); static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::getPosPdata"); PyObject *_retval = 0; { ArgLocker _lock; ParticleDataImpl<Vec3>& target = *_args.getPtr<ParticleDataImpl<Vec3> >("target",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->getPosPdata(target); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::getPosPdata"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::getPosPdata",e.what()); return 0; } } + void setPosPdata(ParticleDataImpl<Vec3>& source); static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::setPosPdata"); PyObject *_retval = 0; { ArgLocker _lock; ParticleDataImpl<Vec3>& source = *_args.getPtr<ParticleDataImpl<Vec3> >("source",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setPosPdata(source); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::setPosPdata"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::setPosPdata",e.what()); return 0; } } + //! transform coordinate system from one grid size to another (usually upon load) + void transformPositions( Vec3i dimOld, Vec3i dimNew ); + + //! explicitly trigger compression from outside + void doCompress() { if ( mDeletes > mDeleteChunk) compress(); } + //! insert buffered positions as new particles, update additional particle data + void insertBufferedParticles(); + //! resize data vector, and all pdata fields + void resizeAll(int newsize); + + // adding and deleting + inline void kill(int idx); + int add(const S& data); + // remove all particles, init 0 length arrays (also pdata) + void clear(); static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::clear"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->clear(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::clear"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::clear",e.what()); return 0; } } + + //! Advect particle in grid velocity field + void advectInGrid(FlagGrid& flags, MACGrid& vel, int integrationMode, bool deleteInObstacle=true ); static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::advectInGrid"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); int integrationMode = _args.get<int >("integrationMode",2,&_lock); bool deleteInObstacle = _args.getOpt<bool >("deleteInObstacle",3,true ,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->advectInGrid(flags,vel,integrationMode,deleteInObstacle); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::advectInGrid"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::advectInGrid",e.what()); return 0; } } + + //! Project particles outside obstacles + void projectOutside(Grid<Vec3>& gradient); static PyObject* _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleSystem* pbo = dynamic_cast<ParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleSystem::projectOutside"); PyObject *_retval = 0; { ArgLocker _lock; Grid<Vec3>& gradient = *_args.getPtr<Grid<Vec3> >("gradient",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->projectOutside(gradient); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleSystem::projectOutside"); return _retval; } catch(std::exception& e) { pbSetError("ParticleSystem::projectOutside",e.what()); return 0; } } + + virtual ParticleBase* clone(); + virtual std::string infoString() const; + + //! debugging + inline void checkPartIndex(int idx) const; + +protected: + //! deletion count , and interval for re-compressing + int mDeletes, mDeleteChunk; + //! the particle data + std::vector<S> mData; + //! reduce storage , called by doCompress + virtual void compress(); public: PbArgs _args;} +#define _C_ParticleSystem +; + +//****************************************************************************** + +//! Simplest data class for particle systems +struct BasicParticleData { +public: + BasicParticleData() : pos(0.), flag(0) {} + BasicParticleData(const Vec3& p) : pos(p), flag(0) {} + static ParticleBase::SystemType getType() { return ParticleBase::PARTICLE; } + + //! data + Vec3 pos; + int flag; +}; + +class BasicParticleSystem : public ParticleSystem<BasicParticleData> {public: + BasicParticleSystem(FluidSolver* parent); static int _W_11 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "BasicParticleSystem::BasicParticleSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new BasicParticleSystem(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"BasicParticleSystem::BasicParticleSystem" ); return 0; } catch(std::exception& e) { pbSetError("BasicParticleSystem::BasicParticleSystem",e.what()); return -1; } } + + //! file io + void save(std::string name); static PyObject* _W_12 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); BasicParticleSystem* pbo = dynamic_cast<BasicParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "BasicParticleSystem::save"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->save(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"BasicParticleSystem::save"); return _retval; } catch(std::exception& e) { pbSetError("BasicParticleSystem::save",e.what()); return 0; } } + void load(std::string name); static PyObject* _W_13 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); BasicParticleSystem* pbo = dynamic_cast<BasicParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "BasicParticleSystem::load"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->load(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"BasicParticleSystem::load"); return _retval; } catch(std::exception& e) { pbSetError("BasicParticleSystem::load",e.what()); return 0; } } + + // save to text file + void writeParticlesText(std::string name); + // other output formats + void writeParticlesRawPositionsGz(std::string name); + void writeParticlesRawVelocityGz(std::string name); + + // add particles in python + void addParticle(Vec3 pos) { add(BasicParticleData(pos)); } static PyObject* _W_14 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); BasicParticleSystem* pbo = dynamic_cast<BasicParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "BasicParticleSystem::addParticle"); PyObject *_retval = 0; { ArgLocker _lock; Vec3 pos = _args.get<Vec3 >("pos",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->addParticle(pos); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"BasicParticleSystem::addParticle"); return _retval; } catch(std::exception& e) { pbSetError("BasicParticleSystem::addParticle",e.what()); return 0; } } + + // dangerous, get low level access - avoid usage, only used in vortex filament advection for now + std::vector<BasicParticleData>& getData() { return mData; } + void printParts(int start=-1, int stop=-1, bool printIndex=false); static PyObject* _W_15 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); BasicParticleSystem* pbo = dynamic_cast<BasicParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "BasicParticleSystem::printParts"); PyObject *_retval = 0; { ArgLocker _lock; int start = _args.getOpt<int >("start",0,-1,&_lock); int stop = _args.getOpt<int >("stop",1,-1,&_lock); bool printIndex = _args.getOpt<bool >("printIndex",2,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->printParts(start,stop,printIndex); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"BasicParticleSystem::printParts"); return _retval; } catch(std::exception& e) { pbSetError("BasicParticleSystem::printParts",e.what()); return 0; } } public: PbArgs _args;} +#define _C_BasicParticleSystem +; + + +//****************************************************************************** + +//! Index into other particle system +// used for grid based neighborhood searches on generic particle systems (stores +// only active particles, and reduces copied data) +// note - pos & flag are disabled here, do not use! +struct ParticleIndexData { +public: + ParticleIndexData() : sourceIndex(0) {} + static ParticleBase::SystemType getType() { return ParticleBase::INDEX; } + + int sourceIndex; // index of this particle in the original particle system + // note - the following two are needed for template instantiation, but not used + // for the particle index system (use values from original one!) + static Vec3 pos; // do not use... + static int flag; // not needed usally + //Vec3 pos; // enable for debugging +}; + +class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> {public: + ParticleIndexSystem(FluidSolver* parent) :ParticleSystem<ParticleIndexData>(parent){} static int _W_16 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ParticleIndexSystem::ParticleIndexSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ParticleIndexSystem(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ParticleIndexSystem::ParticleIndexSystem" ); return 0; } catch(std::exception& e) { pbSetError("ParticleIndexSystem::ParticleIndexSystem",e.what()); return -1; } }; + //! we only need a resize function... + void resize(int size) { mData.resize(size); } public: PbArgs _args;} +#define _C_ParticleIndexSystem +; + + + +//****************************************************************************** + +//! Particle set with connectivity + +template<class DATA, class CON> class ConnectedParticleSystem : public ParticleSystem<DATA> {public: + ConnectedParticleSystem(FluidSolver* parent) :ParticleSystem<DATA>(parent){} static int _W_17 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ConnectedParticleSystem::ConnectedParticleSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ConnectedParticleSystem(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ConnectedParticleSystem::ConnectedParticleSystem" ); return 0; } catch(std::exception& e) { pbSetError("ConnectedParticleSystem::ConnectedParticleSystem",e.what()); return -1; } } + + // accessors + inline bool isSegActive(int i) { return (mSegments[i].flag & ParticleBase::PDELETE) == 0; } + inline int segSize() const { return mSegments.size(); } + inline CON& seg(int i) { return mSegments[i]; } + inline const CON& seg(int i) const { return mSegments[i]; } + + virtual ParticleBase* clone(); + +protected: + std::vector<CON> mSegments; virtual void compress(); public: PbArgs _args;} +#define _C_ConnectedParticleSystem +; + +//****************************************************************************** + +//! abstract interface for particle data +class ParticleDataBase : public PbClass {public: + ParticleDataBase(FluidSolver* parent); static int _W_18 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ParticleDataBase::ParticleDataBase" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ParticleDataBase(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ParticleDataBase::ParticleDataBase" ); return 0; } catch(std::exception& e) { pbSetError("ParticleDataBase::ParticleDataBase",e.what()); return -1; } } + virtual ~ParticleDataBase(); + + // data type IDs, in line with those for grids + enum PdataType { TypeNone = 0, TypeReal = 1, TypeInt = 2, TypeVec3 = 4 }; + + // interface functions, using assert instead of pure virtual for python compatibility + virtual int getSizeSlow() const { assertMsg( false , "Dont use, override..."); return 0; } + virtual void addEntry() { assertMsg( false , "Dont use, override..."); return; } + virtual ParticleDataBase* clone() { assertMsg( false , "Dont use, override..."); return NULL; } + virtual PdataType getType() const { assertMsg( false , "Dont use, override..."); return TypeNone; } + virtual void resize(int size) { assertMsg( false , "Dont use, override..."); return; } + virtual void copyValueSlow(int from, int to) { assertMsg( false , "Dont use, override..."); return; } + + //! set base pointer + void setParticleSys(ParticleBase* set) { mpParticleSys = set; } + + //! debugging + inline void checkPartIndex(int idx) const; + +protected: ParticleBase* mpParticleSys; public: PbArgs _args;} +#define _C_ParticleDataBase +; + + +//! abstract interface for particle data + +template<class T> class ParticleDataImpl : public ParticleDataBase {public: + ParticleDataImpl(FluidSolver* parent); static int _W_19 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "ParticleDataImpl::ParticleDataImpl" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new ParticleDataImpl(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"ParticleDataImpl::ParticleDataImpl" ); return 0; } catch(std::exception& e) { pbSetError("ParticleDataImpl::ParticleDataImpl",e.what()); return -1; } } + ParticleDataImpl(FluidSolver* parent, ParticleDataImpl<T>* other); + virtual ~ParticleDataImpl(); + + //! access data + inline T& get(int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const T get(int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline T& operator[](int idx) { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + inline const T operator[](int idx) const { DEBUG_ONLY(checkPartIndex(idx)); return mData[idx]; } + + // set all values to 0, note - different from particleSystem::clear! doesnt modify size of array (has to stay in sync with parent system) + void clear(); static PyObject* _W_20 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::clear"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->clear(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::clear"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::clear",e.what()); return 0; } } + + //! set grid from which to get data... + void setSource(Grid<T>* grid, bool isMAC=false ); static PyObject* _W_21 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::setSource"); PyObject *_retval = 0; { ArgLocker _lock; Grid<T>* grid = _args.getPtr<Grid<T> >("grid",0,&_lock); bool isMAC = _args.getOpt<bool >("isMAC",1,false ,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setSource(grid,isMAC); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::setSource"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::setSource",e.what()); return 0; } } + + // particle data base interface + virtual int getSizeSlow() const; + virtual void addEntry(); + virtual ParticleDataBase* clone(); + virtual PdataType getType() const; + virtual void resize(int s); + virtual void copyValueSlow(int from, int to); + + int size() const { return mData.size(); } + + // fast inlined functions for per particle operations + inline void copyValue(int from, int to) { get(to) = get(from); } + void initNewValue(int idx, Vec3 pos); + + // python interface (similar to grid data) + void setConst(T s); static PyObject* _W_22 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::setConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::setConst"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::setConst",e.what()); return 0; } } + ParticleDataImpl<T>& copyFrom(const ParticleDataImpl<T>& a); static PyObject* _W_23 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::copyFrom"); PyObject *_retval = 0; { ArgLocker _lock; const ParticleDataImpl<T>& a = *_args.getPtr<ParticleDataImpl<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = toPy(pbo->copyFrom(a)); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::copyFrom"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::copyFrom",e.what()); return 0; } } + void add(const ParticleDataImpl<T>& a); static PyObject* _W_24 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::add"); PyObject *_retval = 0; { ArgLocker _lock; const ParticleDataImpl<T>& a = *_args.getPtr<ParticleDataImpl<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->add(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::add"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::add",e.what()); return 0; } } + void sub(const ParticleDataImpl<T>& a); static PyObject* _W_25 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::sub"); PyObject *_retval = 0; { ArgLocker _lock; const ParticleDataImpl<T>& a = *_args.getPtr<ParticleDataImpl<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->sub(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::sub"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::sub",e.what()); return 0; } } + void addConst(T s); static PyObject* _W_26 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::addConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->addConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::addConst"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::addConst",e.what()); return 0; } } + void addScaled(const ParticleDataImpl<T>& a, const T& factor); static PyObject* _W_27 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::addScaled"); PyObject *_retval = 0; { ArgLocker _lock; const ParticleDataImpl<T>& a = *_args.getPtr<ParticleDataImpl<T> >("a",0,&_lock); const T& factor = *_args.getPtr<T >("factor",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->addScaled(a,factor); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::addScaled"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::addScaled",e.what()); return 0; } } + void mult( const ParticleDataImpl<T>& a); static PyObject* _W_28 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::mult"); PyObject *_retval = 0; { ArgLocker _lock; const ParticleDataImpl<T>& a = *_args.getPtr<ParticleDataImpl<T> >("a",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->mult(a); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::mult"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::mult",e.what()); return 0; } } + void multConst(T s); static PyObject* _W_29 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::multConst"); PyObject *_retval = 0; { ArgLocker _lock; T s = _args.get<T >("s",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->multConst(s); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::multConst"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::multConst",e.what()); return 0; } } + void clamp(Real min, Real max); static PyObject* _W_30 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::clamp"); PyObject *_retval = 0; { ArgLocker _lock; Real min = _args.get<Real >("min",0,&_lock); Real max = _args.get<Real >("max",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->clamp(min,max); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::clamp"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::clamp",e.what()); return 0; } } + Real getMaxAbsValue(); static PyObject* _W_31 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::getMaxAbsValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMaxAbsValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::getMaxAbsValue"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::getMaxAbsValue",e.what()); return 0; } } + Real getMaxValue(); static PyObject* _W_32 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::getMaxValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMaxValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::getMaxValue"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::getMaxValue",e.what()); return 0; } } + Real getMinValue(); static PyObject* _W_33 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::getMinValue"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getMinValue()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::getMinValue"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::getMinValue",e.what()); return 0; } } + + void printPdata(int start=-1, int stop=-1, bool printIndex=false); static PyObject* _W_34 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::printPdata"); PyObject *_retval = 0; { ArgLocker _lock; int start = _args.getOpt<int >("start",0,-1,&_lock); int stop = _args.getOpt<int >("stop",1,-1,&_lock); bool printIndex = _args.getOpt<bool >("printIndex",2,false,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->printPdata(start,stop,printIndex); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::printPdata"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::printPdata",e.what()); return 0; } } + + //! file io + void save(std::string name); static PyObject* _W_35 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::save"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->save(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::save"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::save",e.what()); return 0; } } + void load(std::string name); static PyObject* _W_36 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); ParticleDataImpl* pbo = dynamic_cast<ParticleDataImpl*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "ParticleDataImpl::load"); PyObject *_retval = 0; { ArgLocker _lock; std::string name = _args.get<std::string >("name",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->load(name); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"ParticleDataImpl::load"); return _retval; } catch(std::exception& e) { pbSetError("ParticleDataImpl::load",e.what()); return 0; } } +protected: + //! data storage + std::vector<T> mData; + + //! optionally , we might have an associated grid from which to grab new data + Grid<T>* mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3 + bool mGridSourceMAC; public: PbArgs _args;} +#define _C_ParticleDataImpl +; + + + + + + +//****************************************************************************** +// Implementation +//****************************************************************************** + +const int DELETE_PART = 20; // chunk size for compression + +void ParticleBase::addBuffered(const Vec3& pos) { + mNewBuffer.push_back(pos); +} + +template<class S> +void ParticleSystem<S>::clear() { + mDeleteChunk = mDeletes = 0; + this->resizeAll(0); // instead of mData.clear +} + +template<class S> +int ParticleSystem<S>::add(const S& data) { + mData.push_back(data); + mDeleteChunk = mData.size() / DELETE_PART; + this->addAllPdata(); + return mData.size()-1; +} + +template<class S> +inline void ParticleSystem<S>::kill(int idx) { + assertMsg(idx>=0 && idx<size(), "Index out of bounds"); + mData[idx].flag |= PDELETE; + if ( (++mDeletes > mDeleteChunk) && (mAllowCompress) ) compress(); +} + +template<class S> +void ParticleSystem<S>::getPosPdata(ParticleDataImpl<Vec3>& target) { + for(int i=0; i<(int)this->size(); ++i) { + target[i] = this->getPos(i); + } +} +template<class S> +void ParticleSystem<S>::setPosPdata(ParticleDataImpl<Vec3>& target) { + for(int i=0; i<(int)this->size(); ++i) { + this->getPos(i) = target[i]; + } +} + +template<class S> +void ParticleSystem<S>::transformPositions( Vec3i dimOld, Vec3i dimNew ) +{ + Vec3 factor = calcGridSizeFactor( dimNew, dimOld ); + for(int i=0; i<(int)this->size(); ++i) { + this->setPos(i, this->getPos(i) * factor ); + } +} + +// check for deletion/invalid position, otherwise return velocity + + + +template <class S> struct GridAdvectKernel : public KernelBase { GridAdvectKernel(std::vector<S>& p, const MACGrid& vel, const FlagGrid& flags, Real dt, bool deleteInObstacle ) : KernelBase(p.size()) ,p(p),vel(vel),flags(flags),dt(dt),deleteInObstacle(deleteInObstacle) ,u((size)) { run(); } inline void op(int idx, std::vector<S>& p, const MACGrid& vel, const FlagGrid& flags, Real dt, bool deleteInObstacle ,std::vector<Vec3> & u) { + if (p[idx].flag & ParticleBase::PDELETE) { + u[idx] =_0; + } else if (!flags.isInBounds(p[idx].pos,1) || flags.isObstacle(p[idx].pos)) { + u[idx] = _0; + + // for simple tracer particles, its convenient to delete particles right away + // for other sim types, eg flip, we can try to fix positions later on + if(deleteInObstacle) + p[idx].flag |= ParticleBase::PDELETE; + } else { + u[idx] = vel.getInterpolated(p[idx].pos) * dt; + } +} inline operator std::vector<Vec3> () { return u; } inline std::vector<Vec3> & getRet() { return u; } inline std::vector<S>& getArg0() { return p; } typedef std::vector<S> type0;inline const MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline const FlagGrid& getArg2() { return flags; } typedef FlagGrid type2;inline Real& getArg3() { return dt; } typedef Real type3;inline bool& getArg4() { return deleteInObstacle; } typedef bool type4; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,vel,flags,dt,deleteInObstacle,u); } std::vector<S>& p; const MACGrid& vel; const FlagGrid& flags; Real dt; bool deleteInObstacle; std::vector<Vec3> u; };; + +// final check after advection to make sure particles haven't escaped +// (similar to particle advection kernel) + +template <class S> struct KnDeleteInObstacle : public KernelBase { KnDeleteInObstacle(std::vector<S>& p, const FlagGrid& flags) : KernelBase(p.size()) ,p(p),flags(flags) { run(); } inline void op(int idx, std::vector<S>& p, const FlagGrid& flags ) { + if (p[idx].flag & ParticleBase::PDELETE) return; + if (!flags.isInBounds(p[idx].pos,1) || flags.isObstacle(p[idx].pos)) { + p[idx].flag |= ParticleBase::PDELETE; + } +} inline std::vector<S>& getArg0() { return p; } typedef std::vector<S> type0;inline const FlagGrid& getArg1() { return flags; } typedef FlagGrid type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags); } std::vector<S>& p; const FlagGrid& flags; }; + +// try to get closer to actual obstacle boundary +static inline Vec3 bisectBacktracePos(const FlagGrid& flags, const Vec3& oldp, const Vec3& newp) +{ + Real s = 0.; + for(int i=1; i<5; ++i) { + Real ds = 1./(Real)(1<<i); + if (!flags.isObstacle( oldp*(1.-(s+ds)) + newp*(s+ds) )) { + s += ds; + } + } + return( oldp*(1.-(s)) + newp*(s) ); +} + +// at least make sure all particles are inside domain + + +template <class S> struct KnClampPositions : public KernelBase { KnClampPositions(std::vector<S>& p, const FlagGrid& flags, ParticleDataImpl<Vec3> *posOld = NULL) : KernelBase(p.size()) ,p(p),flags(flags),posOld(posOld) { run(); } inline void op(int idx, std::vector<S>& p, const FlagGrid& flags, ParticleDataImpl<Vec3> *posOld = NULL ) { + if (p[idx].flag & ParticleBase::PDELETE) return; + if (!flags.isInBounds(p[idx].pos,0) ) { + p[idx].pos = clamp( p[idx].pos, Vec3(0.), toVec3(flags.getSize())-Vec3(1.) ); + } + if (flags.isObstacle(p[idx].pos)) { + p[idx].pos = bisectBacktracePos(flags, (*posOld)[idx], p[idx].pos); + } +} inline std::vector<S>& getArg0() { return p; } typedef std::vector<S> type0;inline const FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline ParticleDataImpl<Vec3> * getArg2() { return posOld; } typedef ParticleDataImpl<Vec3> type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,posOld); } std::vector<S>& p; const FlagGrid& flags; ParticleDataImpl<Vec3> * posOld; }; + +// advection plugin +template<class S> +void ParticleSystem<S>::advectInGrid(FlagGrid& flags, MACGrid& vel, int integrationMode, bool deleteInObstacle ) { + // position clamp requires old positions, backup + ParticleDataImpl<Vec3> *posOld = NULL; + if(!deleteInObstacle) { + posOld = new ParticleDataImpl<Vec3>(this->getParent()); + posOld->resize(mData.size()); + for(int i=0; i<mData.size();++i) (*posOld)[i] = mData[i].pos; + } + + // update positions + GridAdvectKernel<S> kernel(mData, vel, flags, getParent()->getDt(), deleteInObstacle ); + integratePointSet(kernel, integrationMode); + + if(!deleteInObstacle) { + KnClampPositions<S> ( mData, flags, posOld ); + delete posOld; + } else { + KnDeleteInObstacle<S>( mData, flags); + } +} + + + +template <class S> struct KnProjectParticles : public KernelBase { KnProjectParticles(ParticleSystem<S>& part, Grid<Vec3>& gradient) : KernelBase(part.size()) ,part(part),gradient(gradient) { run(); } inline void op(int idx, ParticleSystem<S>& part, Grid<Vec3>& gradient ) { + static RandomStream rand (3123984); + const double jlen = 0.1; + + if (part.isActive(idx)) { + // project along levelset gradient + Vec3 p = part[idx].pos; + if (gradient.isInBounds(p)) { + Vec3 n = gradient.getInterpolated(p); + Real dist = normalize(n); + Vec3 dx = n * (-dist + jlen * (1 + rand.getReal())); + p += dx; + } + // clamp to outer boundaries (+jitter) + const double jlen = 0.1; + Vec3 jitter = jlen * rand.getVec3(); + part[idx].pos = clamp(p, Vec3(1,1,1)+jitter, toVec3(gradient.getSize()-1)-jitter); + } +} inline ParticleSystem<S>& getArg0() { return part; } typedef ParticleSystem<S> type0;inline Grid<Vec3>& getArg1() { return gradient; } typedef Grid<Vec3> type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, part,gradient); } ParticleSystem<S>& part; Grid<Vec3>& gradient; }; + +template<class S> +void ParticleSystem<S>::projectOutside(Grid<Vec3>& gradient) { + KnProjectParticles<S>(*this, gradient); +} + +template<class S> +void ParticleSystem<S>::resizeAll(int size) { + // resize all buffers to target size in 1 go + mData.resize(size); + for(int i=0; i<(int)mPartData.size(); ++i) + mPartData[i]->resize(size); +} + +template<class S> +void ParticleSystem<S>::compress() { + int nextRead = mData.size(); + for (int i=0; i<(int)mData.size(); i++) { + while ((mData[i].flag & PDELETE) != 0) { + nextRead--; + mData[i] = mData[nextRead]; + // ugly, but prevent virtual function calls here: + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) mPdataReal[pd]->copyValue(nextRead, i); + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) mPdataVec3[pd]->copyValue(nextRead, i); + for(int pd=0; pd<(int)mPdataInt .size(); ++pd) mPdataInt [pd]->copyValue(nextRead, i); + mData[nextRead].flag = PINVALID; + } + } + if(nextRead<(int)mData.size()) debMsg("Deleted "<<((int)mData.size() - nextRead)<<" particles", 1); // debug info + + resizeAll(nextRead); + mDeletes = 0; + mDeleteChunk = mData.size() / DELETE_PART; +} + +//! insert buffered positions as new particles, update additional particle data +template<class S> +void ParticleSystem<S>::insertBufferedParticles() { + if(mNewBuffer.size()==0) return; + int newCnt = mData.size(); + resizeAll(newCnt + mNewBuffer.size()); + + // clear new flag everywhere + for(int i=0; i<(int)mData.size(); ++i) mData[i].flag &= ~PNEW; + + for(int i=0; i<(int)mNewBuffer.size(); ++i) { + // note, other fields are not initialized here... + mData[newCnt].pos = mNewBuffer[i]; + mData[newCnt].flag = PNEW; + // now init pdata fields from associated grids... + for(int pd=0; pd<(int)mPdataReal.size(); ++pd) + mPdataReal[pd]->initNewValue(newCnt, mNewBuffer[i] ); + for(int pd=0; pd<(int)mPdataVec3.size(); ++pd) + mPdataVec3[pd]->initNewValue(newCnt, mNewBuffer[i] ); + for(int pd=0; pd<(int)mPdataInt.size(); ++pd) + mPdataInt[pd]->initNewValue(newCnt, mNewBuffer[i] ); + newCnt++; + } + if(mNewBuffer.size()>0) debMsg("Added & initialized "<<(int)mNewBuffer.size()<<" particles", 1); // debug info + mNewBuffer.clear(); +} + + +template<class DATA, class CON> +void ConnectedParticleSystem<DATA,CON>::compress() { + const int sz = ParticleSystem<DATA>::size(); + int *renumber_back = new int[sz]; + int *renumber = new int[sz]; + for (int i=0; i<sz; i++) + renumber[i] = renumber_back[i] = -1; + + // reorder elements + std::vector<DATA>& data = ParticleSystem<DATA>::mData; + int nextRead = sz; + for (int i=0; i<nextRead; i++) { + if ((data[i].flag & ParticleBase::PDELETE) != 0) { + nextRead--; + data[i] = data[nextRead]; + data[nextRead].flag = 0; + renumber_back[i] = nextRead; + } else + renumber_back[i] = i; + } + + // acceleration structure + for (int i=0; i<nextRead; i++) + renumber[renumber_back[i]] = i; + + // rename indices in filaments + for (int i=0; i<(int)mSegments.size(); i++) + mSegments[i].renumber(renumber); + + ParticleSystem<DATA>::mData.resize(nextRead); + ParticleSystem<DATA>::mDeletes = 0; + ParticleSystem<DATA>::mDeleteChunk = ParticleSystem<DATA>::size() / DELETE_PART; + + delete[] renumber; + delete[] renumber_back; +} + +template<class S> +ParticleBase* ParticleSystem<S>::clone() { + ParticleSystem<S>* nm = new ParticleSystem<S>(getParent()); + if(this->mAllowCompress) compress(); + + nm->mData = mData; + nm->setName(getName()); + this->cloneParticleData(nm); + return nm; +} + +template<class DATA,class CON> +ParticleBase* ConnectedParticleSystem<DATA,CON>::clone() { + ConnectedParticleSystem<DATA,CON>* nm = new ConnectedParticleSystem<DATA,CON>(this->getParent()); + if(this->mAllowCompress) compress(); + + nm->mData = this->mData; + nm->mSegments = mSegments; + nm->setName(this->getName()); + this->cloneParticleData(nm); + return nm; +} + +template<class S> +std::string ParticleSystem<S>::infoString() const { + std::stringstream s; + s << "ParticleSys '" << getName() << "' [" << size() << " parts"; + if(this->getNumPdata()>0) s<< " "<< this->getNumPdata()<<" pd"; + s << "]"; + //for(int i=0; i<(int)mPartData.size(); ++i) { sstr << i<<":" << mPartData[i]->size() <<" "; } + return s.str(); +} + +template<class S> +inline void ParticleSystem<S>::checkPartIndex(int idx) const { + int mySize = this->size(); + if (idx<0 || idx > mySize ) { + errMsg( "ParticleBase " << " size " << mySize << " : index " << idx << " out of bound " ); + } +} + +inline void ParticleDataBase::checkPartIndex(int idx) const { + int mySize = this->getSizeSlow(); + if (idx<0 || idx > mySize ) { + errMsg( "ParticleData " << " size " << mySize << " : index " << idx << " out of bound " ); + } + if ( mpParticleSys && mpParticleSys->getSizeSlow()!=mySize ) { + errMsg( "ParticleData " << " size " << mySize << " does not match parent! (" << mpParticleSys->getSizeSlow() << ") " ); + } +} + +// set contents to zero, as for a grid +template<class T> +void ParticleDataImpl<T>::clear() { + for(int i=0; i<(int)mData.size(); ++i) mData[i] = 0.; +} + + +} // namespace + +#endif + + + diff --git a/source/blender/python/manta_pp/particle.h.reg b/source/blender/python/manta_pp/particle.h.reg new file mode 100644 index 00000000000..1468d7d3e89 --- /dev/null +++ b/source/blender/python/manta_pp/particle.h.reg @@ -0,0 +1,58 @@ +#include "particle.h" ++ParticleBase^ static const Pb::Register _R_$IDX$ ("ParticleBase","ParticleBase","PbClass"); template<> const char* Namify<ParticleBase >::S = "ParticleBase"; +>ParticleBase^ ++ParticleBase^ static const Pb::Register _R_$IDX$ ("ParticleBase","ParticleBase",ParticleBase::_W_0); ++ParticleBase^ static const Pb::Register _R_$IDX$ ("ParticleBase","create",ParticleBase::_W_1); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","ParticleSystem<$CT$>","ParticleBase"); template<> const char* Namify<ParticleSystem<$CT$> >::S = "ParticleSystem<$CT$>"; ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","ParticleSystem",ParticleSystem<$CT$>::_W_2); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","size",ParticleSystem<$CT$>::_W_3); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","setPos",ParticleSystem<$CT$>::_W_4); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","getPos",ParticleSystem<$CT$>::_W_5); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","getPosPdata",ParticleSystem<$CT$>::_W_6); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","setPosPdata",ParticleSystem<$CT$>::_W_7); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","clear",ParticleSystem<$CT$>::_W_8); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","advectInGrid",ParticleSystem<$CT$>::_W_9); ++ParticleSystem^ static const Pb::Register _R_$IDX$ ("ParticleSystem<$CT$>","projectOutside",ParticleSystem<$CT$>::_W_10); ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","BasicParticleSystem","ParticleSystem<$BT$>"); template<> const char* Namify<BasicParticleSystem >::S = "BasicParticleSystem"; +>BasicParticleSystem^ +@BasicParticleSystem^^ParticleSystem^BasicParticleData ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","BasicParticleSystem",BasicParticleSystem::_W_11); ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","save",BasicParticleSystem::_W_12); ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","load",BasicParticleSystem::_W_13); ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","addParticle",BasicParticleSystem::_W_14); ++BasicParticleSystem^ static const Pb::Register _R_$IDX$ ("BasicParticleSystem","printParts",BasicParticleSystem::_W_15); ++ParticleIndexSystem^ static const Pb::Register _R_$IDX$ ("ParticleIndexSystem","ParticleIndexSystem","ParticleSystem<$BT$>"); template<> const char* Namify<ParticleIndexSystem >::S = "ParticleIndexSystem"; +>ParticleIndexSystem^ +@ParticleIndexSystem^^ParticleSystem^ParticleIndexData ++ParticleIndexSystem^ static const Pb::Register _R_$IDX$ ("ParticleIndexSystem","ParticleIndexSystem",ParticleIndexSystem::_W_16); ++ConnectedParticleSystem^ static const Pb::Register _R_$IDX$ ("ConnectedParticleSystem<$CT$>","ConnectedParticleSystem<$CT$>","ParticleSystem<$BT$>"); template<> const char* Namify<ConnectedParticleSystem<$CT$> >::S = "ConnectedParticleSystem<$CT$>"; +@ConnectedParticleSystem^DATA,CON^ParticleSystem^DATA ++ConnectedParticleSystem^ static const Pb::Register _R_$IDX$ ("ConnectedParticleSystem<$CT$>","ConnectedParticleSystem",ConnectedParticleSystem<$CT$>::_W_17); ++ParticleDataBase^ static const Pb::Register _R_$IDX$ ("ParticleDataBase","ParticleDataBase","PbClass"); template<> const char* Namify<ParticleDataBase >::S = "ParticleDataBase"; +>ParticleDataBase^ ++ParticleDataBase^ static const Pb::Register _R_$IDX$ ("ParticleDataBase","ParticleDataBase",ParticleDataBase::_W_18); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","ParticleDataImpl<$CT$>","ParticleDataBase"); template<> const char* Namify<ParticleDataImpl<$CT$> >::S = "ParticleDataImpl<$CT$>"; ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","ParticleDataImpl",ParticleDataImpl<$CT$>::_W_19); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","clear",ParticleDataImpl<$CT$>::_W_20); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","setSource",ParticleDataImpl<$CT$>::_W_21); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","setConst",ParticleDataImpl<$CT$>::_W_22); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","copyFrom",ParticleDataImpl<$CT$>::_W_23); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","add",ParticleDataImpl<$CT$>::_W_24); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","sub",ParticleDataImpl<$CT$>::_W_25); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","addConst",ParticleDataImpl<$CT$>::_W_26); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","addScaled",ParticleDataImpl<$CT$>::_W_27); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","mult",ParticleDataImpl<$CT$>::_W_28); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","multConst",ParticleDataImpl<$CT$>::_W_29); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","clamp",ParticleDataImpl<$CT$>::_W_30); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","getMaxAbsValue",ParticleDataImpl<$CT$>::_W_31); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","getMaxValue",ParticleDataImpl<$CT$>::_W_32); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","getMinValue",ParticleDataImpl<$CT$>::_W_33); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","printPdata",ParticleDataImpl<$CT$>::_W_34); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","save",ParticleDataImpl<$CT$>::_W_35); ++ParticleDataImpl^ static const Pb::Register _R_$IDX$ ("ParticleDataImpl<$CT$>","load",ParticleDataImpl<$CT$>::_W_36); +>ParticleDataImpl^int +&static const Pb::Register _R_$IDX$ ("ParticleDataImpl<int>","PdataInt",""); +>ParticleDataImpl^Real +&static const Pb::Register _R_$IDX$ ("ParticleDataImpl<Real>","PdataReal",""); +>ParticleDataImpl^Vec3 +&static const Pb::Register _R_$IDX$ ("ParticleDataImpl<Vec3>","PdataVec3",""); diff --git a/source/blender/python/manta_pp/particle.h.reg.cpp b/source/blender/python/manta_pp/particle.h.reg.cpp new file mode 100644 index 00000000000..5d8d832f33b --- /dev/null +++ b/source/blender/python/manta_pp/particle.h.reg.cpp @@ -0,0 +1,119 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "particle.h" +namespace Manta { +#ifdef _C_BasicParticleSystem + static const Pb::Register _R_3 ("BasicParticleSystem","BasicParticleSystem","ParticleSystem<BasicParticleData>"); template<> const char* Namify<BasicParticleSystem >::S = "BasicParticleSystem"; + static const Pb::Register _R_4 ("BasicParticleSystem","BasicParticleSystem",BasicParticleSystem::_W_11); + static const Pb::Register _R_5 ("BasicParticleSystem","save",BasicParticleSystem::_W_12); + static const Pb::Register _R_6 ("BasicParticleSystem","load",BasicParticleSystem::_W_13); + static const Pb::Register _R_7 ("BasicParticleSystem","addParticle",BasicParticleSystem::_W_14); + static const Pb::Register _R_8 ("BasicParticleSystem","printParts",BasicParticleSystem::_W_15); +#endif +#ifdef _C_ParticleBase + static const Pb::Register _R_9 ("ParticleBase","ParticleBase","PbClass"); template<> const char* Namify<ParticleBase >::S = "ParticleBase"; + static const Pb::Register _R_10 ("ParticleBase","ParticleBase",ParticleBase::_W_0); + static const Pb::Register _R_11 ("ParticleBase","create",ParticleBase::_W_1); +#endif +#ifdef _C_ParticleDataBase + static const Pb::Register _R_12 ("ParticleDataBase","ParticleDataBase","PbClass"); template<> const char* Namify<ParticleDataBase >::S = "ParticleDataBase"; + static const Pb::Register _R_13 ("ParticleDataBase","ParticleDataBase",ParticleDataBase::_W_18); +#endif +#ifdef _C_ParticleDataImpl + static const Pb::Register _R_14 ("ParticleDataImpl<int>","ParticleDataImpl<int>","ParticleDataBase"); template<> const char* Namify<ParticleDataImpl<int> >::S = "ParticleDataImpl<int>"; + static const Pb::Register _R_15 ("ParticleDataImpl<int>","ParticleDataImpl",ParticleDataImpl<int>::_W_19); + static const Pb::Register _R_16 ("ParticleDataImpl<int>","clear",ParticleDataImpl<int>::_W_20); + static const Pb::Register _R_17 ("ParticleDataImpl<int>","setSource",ParticleDataImpl<int>::_W_21); + static const Pb::Register _R_18 ("ParticleDataImpl<int>","setConst",ParticleDataImpl<int>::_W_22); + static const Pb::Register _R_19 ("ParticleDataImpl<int>","copyFrom",ParticleDataImpl<int>::_W_23); + static const Pb::Register _R_20 ("ParticleDataImpl<int>","add",ParticleDataImpl<int>::_W_24); + static const Pb::Register _R_21 ("ParticleDataImpl<int>","sub",ParticleDataImpl<int>::_W_25); + static const Pb::Register _R_22 ("ParticleDataImpl<int>","addConst",ParticleDataImpl<int>::_W_26); + static const Pb::Register _R_23 ("ParticleDataImpl<int>","addScaled",ParticleDataImpl<int>::_W_27); + static const Pb::Register _R_24 ("ParticleDataImpl<int>","mult",ParticleDataImpl<int>::_W_28); + static const Pb::Register _R_25 ("ParticleDataImpl<int>","multConst",ParticleDataImpl<int>::_W_29); + static const Pb::Register _R_26 ("ParticleDataImpl<int>","clamp",ParticleDataImpl<int>::_W_30); + static const Pb::Register _R_27 ("ParticleDataImpl<int>","getMaxAbsValue",ParticleDataImpl<int>::_W_31); + static const Pb::Register _R_28 ("ParticleDataImpl<int>","getMaxValue",ParticleDataImpl<int>::_W_32); + static const Pb::Register _R_29 ("ParticleDataImpl<int>","getMinValue",ParticleDataImpl<int>::_W_33); + static const Pb::Register _R_30 ("ParticleDataImpl<int>","printPdata",ParticleDataImpl<int>::_W_34); + static const Pb::Register _R_31 ("ParticleDataImpl<int>","save",ParticleDataImpl<int>::_W_35); + static const Pb::Register _R_32 ("ParticleDataImpl<int>","load",ParticleDataImpl<int>::_W_36); + static const Pb::Register _R_33 ("ParticleDataImpl<Real>","ParticleDataImpl<Real>","ParticleDataBase"); template<> const char* Namify<ParticleDataImpl<Real> >::S = "ParticleDataImpl<Real>"; + static const Pb::Register _R_34 ("ParticleDataImpl<Real>","ParticleDataImpl",ParticleDataImpl<Real>::_W_19); + static const Pb::Register _R_35 ("ParticleDataImpl<Real>","clear",ParticleDataImpl<Real>::_W_20); + static const Pb::Register _R_36 ("ParticleDataImpl<Real>","setSource",ParticleDataImpl<Real>::_W_21); + static const Pb::Register _R_37 ("ParticleDataImpl<Real>","setConst",ParticleDataImpl<Real>::_W_22); + static const Pb::Register _R_38 ("ParticleDataImpl<Real>","copyFrom",ParticleDataImpl<Real>::_W_23); + static const Pb::Register _R_39 ("ParticleDataImpl<Real>","add",ParticleDataImpl<Real>::_W_24); + static const Pb::Register _R_40 ("ParticleDataImpl<Real>","sub",ParticleDataImpl<Real>::_W_25); + static const Pb::Register _R_41 ("ParticleDataImpl<Real>","addConst",ParticleDataImpl<Real>::_W_26); + static const Pb::Register _R_42 ("ParticleDataImpl<Real>","addScaled",ParticleDataImpl<Real>::_W_27); + static const Pb::Register _R_43 ("ParticleDataImpl<Real>","mult",ParticleDataImpl<Real>::_W_28); + static const Pb::Register _R_44 ("ParticleDataImpl<Real>","multConst",ParticleDataImpl<Real>::_W_29); + static const Pb::Register _R_45 ("ParticleDataImpl<Real>","clamp",ParticleDataImpl<Real>::_W_30); + static const Pb::Register _R_46 ("ParticleDataImpl<Real>","getMaxAbsValue",ParticleDataImpl<Real>::_W_31); + static const Pb::Register _R_47 ("ParticleDataImpl<Real>","getMaxValue",ParticleDataImpl<Real>::_W_32); + static const Pb::Register _R_48 ("ParticleDataImpl<Real>","getMinValue",ParticleDataImpl<Real>::_W_33); + static const Pb::Register _R_49 ("ParticleDataImpl<Real>","printPdata",ParticleDataImpl<Real>::_W_34); + static const Pb::Register _R_50 ("ParticleDataImpl<Real>","save",ParticleDataImpl<Real>::_W_35); + static const Pb::Register _R_51 ("ParticleDataImpl<Real>","load",ParticleDataImpl<Real>::_W_36); + static const Pb::Register _R_52 ("ParticleDataImpl<Vec3>","ParticleDataImpl<Vec3>","ParticleDataBase"); template<> const char* Namify<ParticleDataImpl<Vec3> >::S = "ParticleDataImpl<Vec3>"; + static const Pb::Register _R_53 ("ParticleDataImpl<Vec3>","ParticleDataImpl",ParticleDataImpl<Vec3>::_W_19); + static const Pb::Register _R_54 ("ParticleDataImpl<Vec3>","clear",ParticleDataImpl<Vec3>::_W_20); + static const Pb::Register _R_55 ("ParticleDataImpl<Vec3>","setSource",ParticleDataImpl<Vec3>::_W_21); + static const Pb::Register _R_56 ("ParticleDataImpl<Vec3>","setConst",ParticleDataImpl<Vec3>::_W_22); + static const Pb::Register _R_57 ("ParticleDataImpl<Vec3>","copyFrom",ParticleDataImpl<Vec3>::_W_23); + static const Pb::Register _R_58 ("ParticleDataImpl<Vec3>","add",ParticleDataImpl<Vec3>::_W_24); + static const Pb::Register _R_59 ("ParticleDataImpl<Vec3>","sub",ParticleDataImpl<Vec3>::_W_25); + static const Pb::Register _R_60 ("ParticleDataImpl<Vec3>","addConst",ParticleDataImpl<Vec3>::_W_26); + static const Pb::Register _R_61 ("ParticleDataImpl<Vec3>","addScaled",ParticleDataImpl<Vec3>::_W_27); + static const Pb::Register _R_62 ("ParticleDataImpl<Vec3>","mult",ParticleDataImpl<Vec3>::_W_28); + static const Pb::Register _R_63 ("ParticleDataImpl<Vec3>","multConst",ParticleDataImpl<Vec3>::_W_29); + static const Pb::Register _R_64 ("ParticleDataImpl<Vec3>","clamp",ParticleDataImpl<Vec3>::_W_30); + static const Pb::Register _R_65 ("ParticleDataImpl<Vec3>","getMaxAbsValue",ParticleDataImpl<Vec3>::_W_31); + static const Pb::Register _R_66 ("ParticleDataImpl<Vec3>","getMaxValue",ParticleDataImpl<Vec3>::_W_32); + static const Pb::Register _R_67 ("ParticleDataImpl<Vec3>","getMinValue",ParticleDataImpl<Vec3>::_W_33); + static const Pb::Register _R_68 ("ParticleDataImpl<Vec3>","printPdata",ParticleDataImpl<Vec3>::_W_34); + static const Pb::Register _R_69 ("ParticleDataImpl<Vec3>","save",ParticleDataImpl<Vec3>::_W_35); + static const Pb::Register _R_70 ("ParticleDataImpl<Vec3>","load",ParticleDataImpl<Vec3>::_W_36); +#endif +#ifdef _C_ParticleIndexSystem + static const Pb::Register _R_71 ("ParticleIndexSystem","ParticleIndexSystem","ParticleSystem<ParticleIndexData>"); template<> const char* Namify<ParticleIndexSystem >::S = "ParticleIndexSystem"; + static const Pb::Register _R_72 ("ParticleIndexSystem","ParticleIndexSystem",ParticleIndexSystem::_W_16); +#endif +#ifdef _C_ParticleSystem + static const Pb::Register _R_73 ("ParticleSystem<BasicParticleData>","ParticleSystem<BasicParticleData>","ParticleBase"); template<> const char* Namify<ParticleSystem<BasicParticleData> >::S = "ParticleSystem<BasicParticleData>"; + static const Pb::Register _R_74 ("ParticleSystem<BasicParticleData>","ParticleSystem",ParticleSystem<BasicParticleData>::_W_2); + static const Pb::Register _R_75 ("ParticleSystem<BasicParticleData>","size",ParticleSystem<BasicParticleData>::_W_3); + static const Pb::Register _R_76 ("ParticleSystem<BasicParticleData>","setPos",ParticleSystem<BasicParticleData>::_W_4); + static const Pb::Register _R_77 ("ParticleSystem<BasicParticleData>","getPos",ParticleSystem<BasicParticleData>::_W_5); + static const Pb::Register _R_78 ("ParticleSystem<BasicParticleData>","getPosPdata",ParticleSystem<BasicParticleData>::_W_6); + static const Pb::Register _R_79 ("ParticleSystem<BasicParticleData>","setPosPdata",ParticleSystem<BasicParticleData>::_W_7); + static const Pb::Register _R_80 ("ParticleSystem<BasicParticleData>","clear",ParticleSystem<BasicParticleData>::_W_8); + static const Pb::Register _R_81 ("ParticleSystem<BasicParticleData>","advectInGrid",ParticleSystem<BasicParticleData>::_W_9); + static const Pb::Register _R_82 ("ParticleSystem<BasicParticleData>","projectOutside",ParticleSystem<BasicParticleData>::_W_10); + static const Pb::Register _R_83 ("ParticleSystem<ParticleIndexData>","ParticleSystem<ParticleIndexData>","ParticleBase"); template<> const char* Namify<ParticleSystem<ParticleIndexData> >::S = "ParticleSystem<ParticleIndexData>"; + static const Pb::Register _R_84 ("ParticleSystem<ParticleIndexData>","ParticleSystem",ParticleSystem<ParticleIndexData>::_W_2); + static const Pb::Register _R_85 ("ParticleSystem<ParticleIndexData>","size",ParticleSystem<ParticleIndexData>::_W_3); + static const Pb::Register _R_86 ("ParticleSystem<ParticleIndexData>","setPos",ParticleSystem<ParticleIndexData>::_W_4); + static const Pb::Register _R_87 ("ParticleSystem<ParticleIndexData>","getPos",ParticleSystem<ParticleIndexData>::_W_5); + static const Pb::Register _R_88 ("ParticleSystem<ParticleIndexData>","getPosPdata",ParticleSystem<ParticleIndexData>::_W_6); + static const Pb::Register _R_89 ("ParticleSystem<ParticleIndexData>","setPosPdata",ParticleSystem<ParticleIndexData>::_W_7); + static const Pb::Register _R_90 ("ParticleSystem<ParticleIndexData>","clear",ParticleSystem<ParticleIndexData>::_W_8); + static const Pb::Register _R_91 ("ParticleSystem<ParticleIndexData>","advectInGrid",ParticleSystem<ParticleIndexData>::_W_9); + static const Pb::Register _R_92 ("ParticleSystem<ParticleIndexData>","projectOutside",ParticleSystem<ParticleIndexData>::_W_10); +#endif +static const Pb::Register _R_0 ("ParticleDataImpl<int>","PdataInt",""); +static const Pb::Register _R_1 ("ParticleDataImpl<Real>","PdataReal",""); +static const Pb::Register _R_2 ("ParticleDataImpl<Vec3>","PdataVec3",""); +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/plugin/advection.cpp b/source/blender/python/manta_pp/plugin/advection.cpp new file mode 100644 index 00000000000..fbf04f0ba2e --- /dev/null +++ b/source/blender/python/manta_pp/plugin/advection.cpp @@ -0,0 +1,337 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugins for pressure correction: + * - solve_pressure + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" + +using namespace std; + +namespace Manta { + +static inline bool isNotFluid(FlagGrid& flags, int i, int j, int k) +{ + if ( flags.isFluid(i,j,k) ) return false; + if ( flags.isFluid(i-1,j,k) ) return false; + if ( flags.isFluid(i,j-1,k) ) return false; + if ( flags.is3D() ) { + if ( flags.isFluid(i,j,k-1) ) return false; + } + return true; +} + +//! Semi-Lagrange interpolation kernel + + +template <class T> struct SemiLagrange : public KernelBase { SemiLagrange(FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& src, Real dt, bool isLevelset) : KernelBase(&flags,1) ,flags(flags),vel(vel),dst(dst),src(src),dt(dt),isLevelset(isLevelset) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& src, Real dt, bool isLevelset ) { + if (flags.isObstacle(i,j,k)) { + dst(i,j,k) = 0; + return; + } + if (!isLevelset && isNotFluid(flags,i,j,k) ) { + dst(i,j,k) = src(i,j,k); + return; + } + + // SL traceback + Vec3 pos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getCentered(i,j,k) * dt; + dst(i,j,k) = src.getInterpolated(pos); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Grid<T>& getArg2() { return dst; } typedef Grid<T> type2;inline Grid<T>& getArg3() { return src; } typedef Grid<T> type3;inline Real& getArg4() { return dt; } typedef Real type4;inline bool& getArg5() { return isLevelset; } typedef bool type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,dst,src,dt,isLevelset); } FlagGrid& flags; MACGrid& vel; Grid<T>& dst; Grid<T>& src; Real dt; bool isLevelset; }; + +static inline bool isNotFluidMAC(FlagGrid& flags, int i, int j, int k) +{ + if ( flags.isFluid(i,j,k) ) return false; + return true; +} + +//! Semi-Lagrange interpolation kernel for MAC grids + + + struct SemiLagrangeMAC : public KernelBase { SemiLagrangeMAC(FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& src, Real dt) : KernelBase(&flags,1) ,flags(flags),vel(vel),dst(dst),src(src),dt(dt) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& src, Real dt ) { + if (flags.isObstacle(i,j,k)) { + dst(i,j,k) = 0; + return; + } + if ( isNotFluidMAC(flags,i,j,k) ) { + dst(i,j,k) = src(i,j,k); + return; + } + + // get currect velocity at MAC position + // no need to shift xpos etc. as lookup field is also shifted + Vec3 xpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACX(i,j,k) * dt; + Real vx = src.getInterpolatedComponent<0>(xpos); + Vec3 ypos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACY(i,j,k) * dt; + Real vy = src.getInterpolatedComponent<1>(ypos); + Vec3 zpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACZ(i,j,k) * dt; + Real vz = src.getInterpolatedComponent<2>(zpos); + + dst(i,j,k) = Vec3(vx,vy,vz); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline MACGrid& getArg2() { return dst; } typedef MACGrid type2;inline MACGrid& getArg3() { return src; } typedef MACGrid type3;inline Real& getArg4() { return dt; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,dst,src,dt); } FlagGrid& flags; MACGrid& vel; MACGrid& dst; MACGrid& src; Real dt; }; + +//! Kernel: Correct based on forward and backward SL steps (for both centered & mac grids) + + + +template <class T> struct MacCormackCorrect : public KernelBase { MacCormackCorrect(FlagGrid& flags, Grid<T>& dst, Grid<T>& old, Grid<T>& fwd, Grid<T>& bwd, Real strength, bool isLevelSet, bool isMAC=false ) : KernelBase(&flags,0) ,flags(flags),dst(dst),old(old),fwd(fwd),bwd(bwd),strength(strength),isLevelSet(isLevelSet),isMAC(isMAC) { run(); } inline void op(int idx, FlagGrid& flags, Grid<T>& dst, Grid<T>& old, Grid<T>& fwd, Grid<T>& bwd, Real strength, bool isLevelSet, bool isMAC=false ) { + // note, replacement for isNotFluidMAC and isNotFluid + bool skip = false; + + if (!flags.isFluid(idx)) skip = true; + if(!isMAC) { + if( (idx>=flags.getStrideX()) && (!flags.isFluid(idx-flags.getStrideX()) )) skip = true; + if( (idx>=flags.getStrideY()) && (!flags.isFluid(idx-flags.getStrideY()) )) skip = true; + if ( flags.is3D() ) { + if( (idx>=flags.getStrideZ()) &&(!flags.isFluid(idx-flags.getStrideZ()) )) skip = true; + } } + if ( skip ) { + dst[idx] = isLevelSet ? fwd[idx] : (T)0.0; + return; + } + + // note, strenth of correction can be modified here + dst[idx] = fwd[idx] + strength * 0.5 * (old[idx] - bwd[idx]); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<T>& getArg1() { return dst; } typedef Grid<T> type1;inline Grid<T>& getArg2() { return old; } typedef Grid<T> type2;inline Grid<T>& getArg3() { return fwd; } typedef Grid<T> type3;inline Grid<T>& getArg4() { return bwd; } typedef Grid<T> type4;inline Real& getArg5() { return strength; } typedef Real type5;inline bool& getArg6() { return isLevelSet; } typedef bool type6;inline bool& getArg7() { return isMAC; } typedef bool type7; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, flags,dst,old,fwd,bwd,strength,isLevelSet,isMAC); } FlagGrid& flags; Grid<T>& dst; Grid<T>& old; Grid<T>& fwd; Grid<T>& bwd; Real strength; bool isLevelSet; bool isMAC; }; + +// Helper to collect min/max in a template +template<class T> inline void getMinMax(T& minv, T& maxv, const T& val) { + if (val < minv) minv = val; + if (val > maxv) maxv = val; +} +template<> inline void getMinMax<Vec3>(Vec3& minv, Vec3& maxv, const Vec3& val) { + getMinMax(minv.x, maxv.x, val.x); + getMinMax(minv.y, maxv.y, val.y); + getMinMax(minv.z, maxv.z, val.z); +} + + +//! Helper function for clamping non-mac grids +template<class T> +inline T doClampComponent(const Vec3i& upperClamp, Grid<T>& orig, T dst, const Vec3i& posFwd) { + // clamp forward lookup to grid + const int i0 = clamp(posFwd.x, 0, upperClamp.x-1); + const int j0 = clamp(posFwd.y, 0, upperClamp.y-1); + const int k0 = clamp(posFwd.z, 0, (orig.is3D() ? (upperClamp.z-1) : 1) ); + const int i1 = i0+1, j1 = j0+1, k1= (orig.is3D() ? (k0+1) : k0); + + if (!orig.isInBounds(Vec3i(i0,j0,k0),1)) { + return dst; + } + + // find min/max around fwd pos + T minv = orig(i0,j0,k0), maxv = minv; + getMinMax(minv, maxv, orig(i1,j0,k0)); + getMinMax(minv, maxv, orig(i0,j1,k0)); + getMinMax(minv, maxv, orig(i1,j1,k0)); + getMinMax(minv, maxv, orig(i0,j0,k1)); + getMinMax(minv, maxv, orig(i1,j0,k1)); + getMinMax(minv, maxv, orig(i0,j1,k1)); + getMinMax(minv, maxv, orig(i1,j1,k1)); + + // write clamped value + return clamp(dst, minv, maxv); +} + +//! Helper function for clamping MAC grids +template<int c> +inline Real doClampComponentMAC(const Vec3i& upperClamp, MACGrid& orig, Real dst, const Vec3i& posFwd) { + // clamp forward lookup to grid + const int i0 = clamp(posFwd.x, 0, upperClamp.x-1); + const int j0 = clamp(posFwd.y, 0, upperClamp.y-1); + const int k0 = clamp(posFwd.z, 0, (orig.is3D() ? (upperClamp.z-1) : 1) ); + const int i1 = i0+1, j1 = j0+1, k1= (orig.is3D() ? (k0+1) : k0); + if (!orig.isInBounds(Vec3i(i0,j0,k0),1)) + return dst; + + // find min/max around fwd pos + Real minv = orig(i0,j0,k0)[c], maxv = minv; + getMinMax(minv, maxv, orig(i1,j0,k0)[c]); + getMinMax(minv, maxv, orig(i0,j1,k0)[c]); + getMinMax(minv, maxv, orig(i1,j1,k0)[c]); + getMinMax(minv, maxv, orig(i0,j0,k1)[c]); + getMinMax(minv, maxv, orig(i1,j0,k1)[c]); + getMinMax(minv, maxv, orig(i0,j1,k1)[c]); + getMinMax(minv, maxv, orig(i1,j1,k1)[c]); + + return clamp(dst, minv, maxv); +} + +//! Kernel: Clamp obtained value to min/max in source area, and reset values that point out of grid or into boundaries +// (note - MAC grids are handled below) + + +template <class T> struct MacCormackClamp : public KernelBase { MacCormackClamp(FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& orig, Grid<T>& fwd, Real dt) : KernelBase(&flags,1) ,flags(flags),vel(vel),dst(dst),orig(orig),fwd(fwd),dt(dt) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& orig, Grid<T>& fwd, Real dt ) { + if (flags.isObstacle(i,j,k)) + return; + if ( isNotFluid(flags,i,j,k) ) { + dst(i,j,k) = fwd(i,j,k); + return; + } + + T dval = dst(i,j,k); + Vec3i upperClamp = flags.getSize() - 1; + + // lookup forward/backward + Vec3i posFwd = toVec3i( Vec3(i,j,k) - vel.getCentered(i,j,k) * dt ); + Vec3i posBwd = toVec3i( Vec3(i,j,k) + vel.getCentered(i,j,k) * dt ); + + dval = doClampComponent<T>(upperClamp, orig, dval, posFwd ); + + // test if lookups point out of grid or into obstacle + if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || + posBwd.x < 0 || posBwd.y < 0 || posBwd.z < 0 || + posFwd.x > upperClamp.x || posFwd.y > upperClamp.y || ((posFwd.z > upperClamp.z)&&flags.is3D()) || + posBwd.x > upperClamp.x || posBwd.y > upperClamp.y || ((posBwd.z > upperClamp.z)&&flags.is3D()) || + flags.isObstacle(posFwd) || flags.isObstacle(posBwd) ) + { + dval = fwd(i,j,k); + } + dst(i,j,k) = dval; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Grid<T>& getArg2() { return dst; } typedef Grid<T> type2;inline Grid<T>& getArg3() { return orig; } typedef Grid<T> type3;inline Grid<T>& getArg4() { return fwd; } typedef Grid<T> type4;inline Real& getArg5() { return dt; } typedef Real type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,dst,orig,fwd,dt); } FlagGrid& flags; MACGrid& vel; Grid<T>& dst; Grid<T>& orig; Grid<T>& fwd; Real dt; }; + +//! Kernel: same as MacCormackClamp above, but specialized version for MAC grids + + + struct MacCormackClampMAC : public KernelBase { MacCormackClampMAC(FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& orig, MACGrid& fwd, Real dt) : KernelBase(&flags,1) ,flags(flags),vel(vel),dst(dst),orig(orig),fwd(fwd),dt(dt) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& orig, MACGrid& fwd, Real dt ) { + if (flags.isObstacle(i,j,k)) + return; + if ( isNotFluidMAC(flags,i,j,k) ) { + dst(i,j,k) = fwd(i,j,k); + return; + } + + Vec3 pos(i,j,k); + Vec3 dval = dst(i,j,k); + Vec3i upperClamp = flags.getSize() - 1; + + // get total fwd lookup + Vec3i posFwd = toVec3i( Vec3(i,j,k) - vel.getCentered(i,j,k) * dt ); + Vec3i posBwd = toVec3i( Vec3(i,j,k) + vel.getCentered(i,j,k) * dt ); + + // clamp individual components + dval.x = doClampComponentMAC<0>(upperClamp, orig, dval.x, toVec3i( pos - vel.getAtMACX(i,j,k) * dt) ); + dval.y = doClampComponentMAC<1>(upperClamp, orig, dval.y, toVec3i( pos - vel.getAtMACY(i,j,k) * dt) ); + dval.z = doClampComponentMAC<2>(upperClamp, orig, dval.z, toVec3i( pos - vel.getAtMACZ(i,j,k) * dt) ); + + // test if lookups point out of grid or into obstacle + if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || + posBwd.x < 0 || posBwd.y < 0 || posBwd.z < 0 || + posFwd.x > upperClamp.x || posFwd.y > upperClamp.y || ((posFwd.z > upperClamp.z)&&flags.is3D()) || + posBwd.x > upperClamp.x || posBwd.y > upperClamp.y || ((posBwd.z > upperClamp.z)&&flags.is3D()) + //|| flags.isObstacle(posFwd) || flags.isObstacle(posBwd) // note - this unfortunately introduces asymmetry... TODO update + ) + { + dval = fwd(i,j,k); + } + + // writeback + dst(i,j,k) = dval; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline MACGrid& getArg2() { return dst; } typedef MACGrid type2;inline MACGrid& getArg3() { return orig; } typedef MACGrid type3;inline MACGrid& getArg4() { return fwd; } typedef MACGrid type4;inline Real& getArg5() { return dt; } typedef Real type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,dst,orig,fwd,dt); } FlagGrid& flags; MACGrid& vel; MACGrid& dst; MACGrid& orig; MACGrid& fwd; Real dt; }; + +//! template function for performing SL advection +template<class GridType> +void fnAdvectSemiLagrange(FluidSolver* parent, FlagGrid& flags, MACGrid& vel, GridType& orig, int order, Real strength) { + typedef typename GridType::BASETYPE T; + + Real dt = parent->getDt(); + bool levelset = orig.getType() & GridBase::TypeLevelset; + + // forward step + GridType fwd(parent); + SemiLagrange<T> (flags, vel, fwd, orig, dt, levelset); + + if (order == 1) { + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + GridType bwd(parent); + GridType newGrid(parent); + + // bwd <- backwards step + SemiLagrange<T> (flags, vel, bwd, fwd, -dt, levelset); + + // newGrid <- compute correction + MacCormackCorrect<T> (flags, newGrid, orig, fwd, bwd, strength, levelset); + + // clamp values + MacCormackClamp<T> (flags, vel, newGrid, orig, fwd, dt); + + orig.swap(newGrid); + } +} + +//! template function for performing SL advection: specialized version for MAC grids +template<> +void fnAdvectSemiLagrange<MACGrid>(FluidSolver* parent, FlagGrid& flags, MACGrid& vel, MACGrid& orig, int order, Real strength) { + Real dt = parent->getDt(); + + // forward step + MACGrid fwd(parent); + SemiLagrangeMAC (flags, vel, fwd, orig, dt); + + if (order == 1) { + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + MACGrid bwd(parent); + MACGrid newGrid(parent); + + // bwd <- backwards step + SemiLagrangeMAC (flags, vel, bwd, fwd, -dt); + + // newGrid <- compute correction + MacCormackCorrect<Vec3> (flags, newGrid, orig, fwd, bwd, strength, false, true); + + // clamp values + MacCormackClampMAC (flags, vel, newGrid, orig, fwd, dt); + + orig.swap(newGrid); + } +} + +//! Perform semi-lagrangian advection of target Real- or Vec3 grid + + +void advectSemiLagrange(FlagGrid* flags, MACGrid* vel, GridBase* grid, int order = 1, Real strength = 1.0) { + assertMsg(order==1 || order==2, "AdvectSemiLagrange: Only order 1 (regular SL) and 2 (MacCormack) supported"); + + // determine type of grid + if (grid->getType() & GridBase::TypeReal) { + fnAdvectSemiLagrange< Grid<Real> >(flags->getParent(), *flags, *vel, *((Grid<Real>*) grid), order, strength); + } + else if (grid->getType() & GridBase::TypeMAC) { + fnAdvectSemiLagrange< MACGrid >(flags->getParent(), *flags, *vel, *((MACGrid*) grid), order, strength); + } + else if (grid->getType() & GridBase::TypeVec3) { + fnAdvectSemiLagrange< Grid<Vec3> >(flags->getParent(), *flags, *vel, *((Grid<Vec3>*) grid), order, strength); + } + else + errMsg("AdvectSemiLagrange: Grid Type is not supported (only Real, Vec3, MAC, Levelset)"); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "advectSemiLagrange" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid* flags = _args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid* vel = _args.getPtr<MACGrid >("vel",1,&_lock); GridBase* grid = _args.getPtr<GridBase >("grid",2,&_lock); int order = _args.getOpt<int >("order",3,1,&_lock); Real strength = _args.getOpt<Real >("strength",4,1.0,&_lock); _retval = getPyNone(); advectSemiLagrange(flags,vel,grid,order,strength); _args.check(); } pbFinalizePlugin(parent,"advectSemiLagrange" ); return _retval; } catch(std::exception& e) { pbSetError("advectSemiLagrange",e.what()); return 0; } } static const Pb::Register _RP_advectSemiLagrange ("","advectSemiLagrange",_W_0); + +} // end namespace DDF + + + diff --git a/source/blender/python/manta_pp/plugin/extforces.cpp b/source/blender/python/manta_pp/plugin/extforces.cpp new file mode 100644 index 00000000000..b0de81ee2cf --- /dev/null +++ b/source/blender/python/manta_pp/plugin/extforces.cpp @@ -0,0 +1,207 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Set boundary conditions, gravity + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "commonkernels.h" + +using namespace std; + +namespace Manta { + +// MLE 2014-07-05 copy from pressure.cpp +inline void convertDescToVec(const string& desc, Vector3D<bool>& lo, Vector3D<bool>& up) { + for(size_t i=0; i<desc.size(); i++) { + if (desc[i] == 'x') lo.x = true; + else if (desc[i] == 'y') lo.y = true; + else if (desc[i] == 'z') lo.z = true; + else if (desc[i] == 'X') up.x = true; + else if (desc[i] == 'Y') up.y = true; + else if (desc[i] == 'Z') up.z = true; + else errMsg("invalid character in boundary description string. Only [xyzXYZ] allowed."); + } +} + +//! add Forces between fl/fl and fl/em cells + struct KnAddForceField : public KernelBase { KnAddForceField(FlagGrid& flags, MACGrid& vel, Grid<Vec3>& force) : KernelBase(&flags,1) ,flags(flags),vel(vel),force(force) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Grid<Vec3>& force ) { + bool curFluid = flags.isFluid(i,j,k); + bool curEmpty = flags.isEmpty(i,j,k); + if (!curFluid && !curEmpty) return; + + if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x += 0.5*(force(i-1,j,k).x + force(i,j,k).x); + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y += 0.5*(force(i,j-1,k).y + force(i,j,k).y); + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z += 0.5*(force(i,j,k-1).z + force(i,j,k).z); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Grid<Vec3>& getArg2() { return force; } typedef Grid<Vec3> type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,force); } FlagGrid& flags; MACGrid& vel; Grid<Vec3>& force; }; + +//! add Forces between fl/fl and fl/em cells + struct KnAddForce : public KernelBase { KnAddForce(FlagGrid& flags, MACGrid& vel, Vec3 force) : KernelBase(&flags,1) ,flags(flags),vel(vel),force(force) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Vec3 force ) { + bool curFluid = flags.isFluid(i,j,k); + bool curEmpty = flags.isEmpty(i,j,k); + if (!curFluid && !curEmpty) return; + + if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x += force.x; + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y += force.y; + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z += force.z; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Vec3& getArg2() { return force; } typedef Vec3 type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,force); } FlagGrid& flags; MACGrid& vel; Vec3 force; }; + +//! add external force fields to all fluid cells +void addForceField(FlagGrid& flags, MACGrid& vel, Grid<Vec3>& force){ + KnAddForceField(flags, vel, force); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addForceField" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); Grid<Vec3>& force = *_args.getPtr<Grid<Vec3> >("force",2,&_lock); _retval = getPyNone(); addForceField(flags,vel,force); _args.check(); } pbFinalizePlugin(parent,"addForceField" ); return _retval; } catch(std::exception& e) { pbSetError("addForceField",e.what()); return 0; } } static const Pb::Register _RP_addForceField ("","addForceField",_W_0); + +//! add gravity forces to all fluid cells +void addGravity(FlagGrid& flags, MACGrid& vel, Vec3 gravity) { + Vec3 f = gravity * flags.getParent()->getDt() / flags.getDx(); + KnAddForce(flags, vel, f); +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addGravity" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); Vec3 gravity = _args.get<Vec3 >("gravity",2,&_lock); _retval = getPyNone(); addGravity(flags,vel,gravity); _args.check(); } pbFinalizePlugin(parent,"addGravity" ); return _retval; } catch(std::exception& e) { pbSetError("addGravity",e.what()); return 0; } } static const Pb::Register _RP_addGravity ("","addGravity",_W_1); + +//! add Buoyancy force based on smoke density + struct KnAddBuoyancy : public KernelBase { KnAddBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 strength) : KernelBase(&flags,1) ,flags(flags),density(density),vel(vel),strength(strength) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 strength ) { + if (!flags.isFluid(i,j,k)) return; + if (flags.isFluid(i-1,j,k)) + vel(i,j,k).x += (0.5 * strength.x) * (density(i,j,k)+density(i-1,j,k)); + if (flags.isFluid(i,j-1,k)) + vel(i,j,k).y += (0.5 * strength.y) * (density(i,j,k)+density(i,j-1,k)); + if (vel.is3D() && flags.isFluid(i,j,k-1)) + vel(i,j,k).z += (0.5 * strength.z) * (density(i,j,k)+density(i,j,k-1)); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return density; } typedef Grid<Real> type1;inline MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline Vec3& getArg3() { return strength; } typedef Vec3 type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,density,vel,strength); } FlagGrid& flags; Grid<Real>& density; MACGrid& vel; Vec3 strength; }; + +//! add Buoyancy force based on smoke density +void addBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 gravity) { + Vec3 f = - gravity * flags.getParent()->getDt() / flags.getParent()->getDx(); + KnAddBuoyancy(flags,density, vel, f); +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addBuoyancy" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",2,&_lock); Vec3 gravity = _args.get<Vec3 >("gravity",3,&_lock); _retval = getPyNone(); addBuoyancy(flags,density,vel,gravity); _args.check(); } pbFinalizePlugin(parent,"addBuoyancy" ); return _retval; } catch(std::exception& e) { pbSetError("addBuoyancy",e.what()); return 0; } } static const Pb::Register _RP_addBuoyancy ("","addBuoyancy",_W_2); + +//! add Buoyancy force based on density and heat field + struct KnAddHeatBuoyancy : public KernelBase { KnAddHeatBuoyancy(FlagGrid& flags, Grid<Real>& density,float densCoeff, MACGrid& vel, Vec3 strength, Grid<Real>& heat, float heatCoeff) : KernelBase(&flags,1) ,flags(flags),density(density),densCoeff(densCoeff),vel(vel),strength(strength),heat(heat),heatCoeff(heatCoeff) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& density,float densCoeff, MACGrid& vel, Vec3 strength, Grid<Real>& heat, float heatCoeff ) { + if (!flags.isFluid(i,j,k)) return; + vel(i,j,k).x += (strength.x) * (densCoeff * density(i,j,k) - heatCoeff * heat(i,j,k)); + vel(i,j,k).y += (strength.y) * (densCoeff * density(i,j,k) - heatCoeff * heat(i,j,k)); + vel(i,j,k).z += (strength.z) * (densCoeff * density(i,j,k) - heatCoeff * heat(i,j,k)); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return density; } typedef Grid<Real> type1;inline float& getArg2() { return densCoeff; } typedef float type2;inline MACGrid& getArg3() { return vel; } typedef MACGrid type3;inline Vec3& getArg4() { return strength; } typedef Vec3 type4;inline Grid<Real>& getArg5() { return heat; } typedef Grid<Real> type5;inline float& getArg6() { return heatCoeff; } typedef float type6; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,density,densCoeff,vel,strength,heat,heatCoeff); } FlagGrid& flags; Grid<Real>& density; float densCoeff; MACGrid& vel; Vec3 strength; Grid<Real>& heat; float heatCoeff; }; +//! add Buoyancy force based on density and heat field +void addHeatBuoyancy(FlagGrid& flags, Grid<Real>& density,float densCoeff, MACGrid& vel, Vec3 gravity, Grid<Real>& heat, float heatCoeff) { + Vec3 f = - gravity; + KnAddHeatBuoyancy(flags,density,densCoeff, vel, f, heat, heatCoeff); +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addHeatBuoyancy" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); float densCoeff = _args.get<float >("densCoeff",2,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",3,&_lock); Vec3 gravity = _args.get<Vec3 >("gravity",4,&_lock); Grid<Real>& heat = *_args.getPtr<Grid<Real> >("heat",5,&_lock); float heatCoeff = _args.get<float >("heatCoeff",6,&_lock); _retval = getPyNone(); addHeatBuoyancy(flags,density,densCoeff,vel,gravity,heat,heatCoeff); _args.check(); } pbFinalizePlugin(parent,"addHeatBuoyancy" ); return _retval; } catch(std::exception& e) { pbSetError("addHeatBuoyancy",e.what()); return 0; } } static const Pb::Register _RP_addHeatBuoyancy ("","addHeatBuoyancy",_W_3); + +//! set no-stick wall boundary condition between ob/fl and ob/ob cells + struct KnSetWallBcs : public KernelBase { KnSetWallBcs(FlagGrid& flags, MACGrid& vel, Vector3D<bool> lo, Vector3D<bool> up, bool admm) : KernelBase(&flags,0) ,flags(flags),vel(vel),lo(lo),up(up),admm(admm) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Vector3D<bool> lo, Vector3D<bool> up, bool admm ) { + + bool curFluid = flags.isFluid(i,j,k); + bool curObstacle = flags.isObstacle(i,j,k); + if (!curFluid && !curObstacle) return; + + // MLE 2014-07-04 + // if not admm, leave it as in orig + // if openBound, don't correct anything (solid is as empty) + // if admm, correct if vel is pointing outwards + + // if "inner" obstacle vel + if(i>0 && curObstacle && !flags.isFluid(i-1,j,k)) vel(i,j,k).x = 0; + if(j>0 && curObstacle && !flags.isFluid(i,j-1,k)) vel(i,j,k).y = 0; + + // check lo.x + if(!lo.x && i>0 && curFluid && flags.isObstacle(i-1,j,k) && ((admm&&vel(i,j,k).x<0)||!admm)) vel(i,j,k).x = 0; + // check up.x + if(!up.x && i>0 && curObstacle && flags.isFluid(i-1,j,k) && ((admm&&vel(i,j,k).x>0)||!admm)) vel(i,j,k).x = 0; + // check lo.y + if(!lo.y && j>0 && curFluid && flags.isObstacle(i,j-1,k) && ((admm&&vel(i,j,k).y<0)||!admm)) vel(i,j,k).y = 0; + // check up.y + if(!up.y && j>0 && curObstacle && flags.isFluid(i,j-1,k) && ((admm&&vel(i,j,k).y>0)||!admm)) vel(i,j,k).y = 0; + // check lo.z + if(!lo.z && k>0 && curFluid && flags.isObstacle(i,j,k-1) && ((admm&&vel(i,j,k).z<0)||!admm)) vel(i,j,k).z = 0; + // check up.z + if(!up.z && k>0 && curObstacle && flags.isFluid(i,j,k-1) && ((admm&&vel(i,j,k).z>0)||!admm)) vel(i,j,k).z = 0; + + + /* MLE consider later + if (curFluid) { + if ((i>0 && flags.isStick(i-1,j,k)) || (i<flags.getSizeX()-1 && flags.isStick(i+1,j,k))) + vel(i,j,k).y = vel(i,j,k).z = 0; + if ((j>0 && flags.isStick(i,j-1,k)) || (j<flags.getSizeY()-1 && flags.isStick(i,j+1,k))) + vel(i,j,k).x = vel(i,j,k).z = 0; + if (vel.is3D() && ((k>0 && flags.isStick(i,j,k-1)) || (k<flags.getSizeZ()-1 && flags.isStick(i,j,k+1)))) + vel(i,j,k).x = vel(i,j,k).y = 0; + } + */ +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Vector3D<bool> & getArg2() { return lo; } typedef Vector3D<bool> type2;inline Vector3D<bool> & getArg3() { return up; } typedef Vector3D<bool> type3;inline bool& getArg4() { return admm; } typedef bool type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,vel,lo,up,admm); } FlagGrid& flags; MACGrid& vel; Vector3D<bool> lo; Vector3D<bool> up; bool admm; }; + +// MLE 2014-07-04 +//! set no-stick boundary condition on walls +void setWallBcs(FlagGrid& flags, MACGrid& vel, string openBound="", bool admm=false) { + Vector3D<bool> lo, up; + convertDescToVec(openBound, lo, up); + KnSetWallBcs(flags, vel, lo, up, admm); +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setWallBcs" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); string openBound = _args.getOpt<string >("openBound",2,"",&_lock); bool admm = _args.getOpt<bool >("admm",3,false,&_lock); _retval = getPyNone(); setWallBcs(flags,vel,openBound,admm); _args.check(); } pbFinalizePlugin(parent,"setWallBcs" ); return _retval; } catch(std::exception& e) { pbSetError("setWallBcs",e.what()); return 0; } } static const Pb::Register _RP_setWallBcs ("","setWallBcs",_W_4); +//! Kernel: gradient norm operator + struct KnConfForce : public KernelBase { KnConfForce(Grid<Vec3>& force, const Grid<Real>& grid, const Grid<Vec3>& curl, Real str) : KernelBase(&force,1) ,force(force),grid(grid),curl(curl),str(str) { run(); } inline void op(int i, int j, int k, Grid<Vec3>& force, const Grid<Real>& grid, const Grid<Vec3>& curl, Real str ) { + Vec3 grad = 0.5 * Vec3( grid(i+1,j,k)-grid(i-1,j,k), + grid(i,j+1,k)-grid(i,j-1,k), 0.); + if(grid.is3D()) grad[2]= 0.5*( grid(i,j,k+1)-grid(i,j,k-1) ); + normalize(grad); + force(i,j,k) = str * cross(grad, curl(i,j,k)); +} inline Grid<Vec3>& getArg0() { return force; } typedef Grid<Vec3> type0;inline const Grid<Real>& getArg1() { return grid; } typedef Grid<Real> type1;inline const Grid<Vec3>& getArg2() { return curl; } typedef Grid<Vec3> type2;inline Real& getArg3() { return str; } typedef Real type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, force,grid,curl,str); } Grid<Vec3>& force; const Grid<Real>& grid; const Grid<Vec3>& curl; Real str; }; + +void vorticityConfinement(MACGrid& vel, FlagGrid& flags, Real strength) { + Grid<Vec3> velCenter(flags.getParent()), curl(flags.getParent()), force(flags.getParent()); + Grid<Real> norm(flags.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GridNorm(norm, curl); + KnConfForce(force, norm, curl, strength); + KnAddForceField(flags, vel, force); +} static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "vorticityConfinement" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",1,&_lock); Real strength = _args.get<Real >("strength",2,&_lock); _retval = getPyNone(); vorticityConfinement(vel,flags,strength); _args.check(); } pbFinalizePlugin(parent,"vorticityConfinement" ); return _retval; } catch(std::exception& e) { pbSetError("vorticityConfinement",e.what()); return 0; } } static const Pb::Register _RP_vorticityConfinement ("","vorticityConfinement",_W_5); + +//! enforce a constant inflow/outflow at the grid boundaries + struct KnSetInflow : public KernelBase { KnSetInflow(MACGrid& vel, int dim, int p0, const Vec3& val) : KernelBase(&vel,0) ,vel(vel),dim(dim),p0(p0),val(val) { run(); } inline void op(int i, int j, int k, MACGrid& vel, int dim, int p0, const Vec3& val ) { + Vec3i p(i,j,k); + if (p[dim] == p0 || p[dim] == p0+1) + vel(i,j,k) = val; +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline int& getArg1() { return dim; } typedef int type1;inline int& getArg2() { return p0; } typedef int type2;inline const Vec3& getArg3() { return val; } typedef Vec3 type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, vel,dim,p0,val); } MACGrid& vel; int dim; int p0; const Vec3& val; }; + +//! enforce a constant inflow/outflow at the grid boundaries +void setInflowBcs(MACGrid& vel, string dir, Vec3 value) { + for(size_t i=0; i<dir.size(); i++) { + if (dir[i] >= 'x' && dir[i] <= 'z') { + int dim = dir[i]-'x'; + KnSetInflow(vel,dim,0,value); + } else if (dir[i] >= 'X' && dir[i] <= 'Z') { + int dim = dir[i]-'X'; + KnSetInflow(vel,dim,vel.getSize()[dim]-1,value); + } else + errMsg("invalid character in direction string. Only [xyzXYZ] allowed."); + } +} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setInflowBcs" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); string dir = _args.get<string >("dir",1,&_lock); Vec3 value = _args.get<Vec3 >("value",2,&_lock); _retval = getPyNone(); setInflowBcs(vel,dir,value); _args.check(); } pbFinalizePlugin(parent,"setInflowBcs" ); return _retval; } catch(std::exception& e) { pbSetError("setInflowBcs",e.what()); return 0; } } static const Pb::Register _RP_setInflowBcs ("","setInflowBcs",_W_6); + +} // namespace + + diff --git a/source/blender/python/manta_pp/plugin/flip.cpp b/source/blender/python/manta_pp/plugin/flip.cpp new file mode 100644 index 00000000000..a0a5a681dc3 --- /dev/null +++ b/source/blender/python/manta_pp/plugin/flip.cpp @@ -0,0 +1,500 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * FLIP (fluid implicit particles) + * for use with particle data fields + * + ******************************************************************************/ + +#include "particle.h" +#include "grid.h" +#include "randomstream.h" +#include "levelset.h" + +using namespace std; +namespace Manta { + + + + +// init + + + +void sampleFlagsWithParticles( FlagGrid& flags, BasicParticleSystem& parts, int discretization, Real randomness ) { + bool is3D = flags.is3D(); + Real jlen = randomness / discretization; + Vec3 disp (1.0 / discretization, 1.0 / discretization, 1.0/discretization); + RandomStream mRand(9832); + + //clear(); + + FOR_IJK_BND(flags, 0) { + if ( flags.isObstacle(i,j,k) ) continue; + if ( flags.isFluid(i,j,k) ) { + Vec3 pos (i,j,k); + for (int dk=0; dk<(is3D ? discretization : 1); dk++) + for (int dj=0; dj<discretization; dj++) + for (int di=0; di<discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5+di, 0.5+dj, 0.5+dk); + subpos += jlen * (Vec3(1,1,1) - 2.0 * mRand.getVec3()); + if(!is3D) subpos[2] = 0.5; + parts.add( BasicParticleData(subpos) ); + } + } + } +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "sampleFlagsWithParticles" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",1,&_lock); int discretization = _args.get<int >("discretization",2,&_lock); Real randomness = _args.get<Real >("randomness",3,&_lock); _retval = getPyNone(); sampleFlagsWithParticles(flags,parts,discretization,randomness); _args.check(); } pbFinalizePlugin(parent,"sampleFlagsWithParticles" ); return _retval; } catch(std::exception& e) { pbSetError("sampleFlagsWithParticles",e.what()); return 0; } } static const Pb::Register _RP_sampleFlagsWithParticles ("","sampleFlagsWithParticles",_W_0); + + + +void sampleLevelsetWithParticles( LevelsetGrid& phi, FlagGrid& flags, BasicParticleSystem& parts, int discretization, Real randomness ) { + bool is3D = phi.is3D(); + Real jlen = randomness / discretization; + Vec3 disp (1.0 / discretization, 1.0 / discretization, 1.0/discretization); + RandomStream mRand(9832); + + //clear(); + + FOR_IJK_BND(phi, 0) { + if ( flags.isObstacle(i,j,k) ) continue; + if ( phi(i,j,k) < 1.733 ) { + Vec3 pos (i,j,k); + for (int dk=0; dk<(is3D ? discretization : 1); dk++) + for (int dj=0; dj<discretization; dj++) + for (int di=0; di<discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5+di, 0.5+dj, 0.5+dk); + subpos += jlen * (Vec3(1,1,1) - 2.0 * mRand.getVec3()); + if(!is3D) subpos[2] = 0.5; + if( phi.getInterpolated(subpos) > 0. ) continue; + parts.add( BasicParticleData(subpos) ); + } + } + } +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "sampleLevelsetWithParticles" ); PyObject *_retval = 0; { ArgLocker _lock; LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",0,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",1,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",2,&_lock); int discretization = _args.get<int >("discretization",3,&_lock); Real randomness = _args.get<Real >("randomness",4,&_lock); _retval = getPyNone(); sampleLevelsetWithParticles(phi,flags,parts,discretization,randomness); _args.check(); } pbFinalizePlugin(parent,"sampleLevelsetWithParticles" ); return _retval; } catch(std::exception& e) { pbSetError("sampleLevelsetWithParticles",e.what()); return 0; } } static const Pb::Register _RP_sampleLevelsetWithParticles ("","sampleLevelsetWithParticles",_W_1); + +void markFluidCells(BasicParticleSystem& parts, FlagGrid& flags) { + // remove all fluid cells + FOR_IJK(flags) { + if (flags.isFluid(i,j,k)) { + flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid; + } + } + + // mark all particles in flaggrid as fluid + for(int idx=0;idx<parts.size();idx++) { + if (!parts.isActive(idx)) continue; + Vec3i p = toVec3i( parts.getPos(idx) ); + if (flags.isInBounds(p) && flags.isEmpty(p)) + flags(p) = (flags(p) | FlagGrid::TypeFluid) & ~FlagGrid::TypeEmpty; + } +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "markFluidCells" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",1,&_lock); _retval = getPyNone(); markFluidCells(parts,flags); _args.check(); } pbFinalizePlugin(parent,"markFluidCells" ); return _retval; } catch(std::exception& e) { pbSetError("markFluidCells",e.what()); return 0; } } static const Pb::Register _RP_markFluidCells ("","markFluidCells",_W_2); + +// for testing purposes only... +void testInitGridWithPos(Grid<Real>& grid) { + FOR_IJK(grid) { grid(i,j,k) = norm( Vec3(i,j,k) ); } +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "testInitGridWithPos" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& grid = *_args.getPtr<Grid<Real> >("grid",0,&_lock); _retval = getPyNone(); testInitGridWithPos(grid); _args.check(); } pbFinalizePlugin(parent,"testInitGridWithPos" ); return _retval; } catch(std::exception& e) { pbSetError("testInitGridWithPos",e.what()); return 0; } } static const Pb::Register _RP_testInitGridWithPos ("","testInitGridWithPos",_W_3); + + +//! helper to calculate particle radius factor to cover the diagonal of a cell in 2d/3d +inline Real calculateRadiusFactor(Grid<Real>& grid, Real factor) { + return (grid.is3D() ? sqrt(3.) : sqrt(2.) ) * (factor+.01); // note, a 1% safety factor is added here +} + +//! re-sample particles based on an input levelset + + +void adjustNumber( BasicParticleSystem& parts, MACGrid& vel, FlagGrid& flags, int minParticles, int maxParticles, LevelsetGrid& phi, Real radiusFactor=1. ) { + // which levelset to use as threshold + const Real SURFACE_LS = -1.0 * calculateRadiusFactor(phi, radiusFactor); + Grid<int> tmp( vel.getParent() ); + std::ostringstream out; + + // count particles in cells, and delete excess particles + for (int idx=0; idx<(int)parts.size(); idx++) { + if (parts.isActive(idx)) { + Vec3i p = toVec3i( parts.getPos(idx) ); + if (!tmp.isInBounds(p) ) { + parts.kill(idx); // out of domain, remove + continue; + } + int num = tmp(p); + + bool atSurface = false; + Real phiv = phi.getInterpolated( parts.getPos(idx) ); + if (phiv > SURFACE_LS) atSurface = true; + + // dont delete particles in non fluid cells here, the particles are "always right" + if ( num > maxParticles && (!atSurface) ) { + parts.kill(idx); + } else { + tmp(p) = num+1; + } + } + } + + // seed new particles + RandomStream mRand(9832); + FOR_IJK(tmp) { + int cnt = tmp(i,j,k); + + // skip cells near surface + if (phi(i,j,k) > SURFACE_LS) continue; + + if (flags.isFluid(i,j,k) && cnt < minParticles) { + for (int m=cnt; m < minParticles; m++) { + Vec3 pos = Vec3(i,j,k) + mRand.getVec3(); + //Vec3 pos (i + 0.5, j + 0.5, k + 0.5); // cell center + parts.addBuffered( pos ); + } + } + } + + parts.doCompress(); + parts.insertBufferedParticles(); +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "adjustNumber" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",2,&_lock); int minParticles = _args.get<int >("minParticles",3,&_lock); int maxParticles = _args.get<int >("maxParticles",4,&_lock); LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",5,&_lock); Real radiusFactor = _args.getOpt<Real >("radiusFactor",6,1. ,&_lock); _retval = getPyNone(); adjustNumber(parts,vel,flags,minParticles,maxParticles,phi,radiusFactor); _args.check(); } pbFinalizePlugin(parent,"adjustNumber" ); return _retval; } catch(std::exception& e) { pbSetError("adjustNumber",e.what()); return 0; } } static const Pb::Register _RP_adjustNumber ("","adjustNumber",_W_4); + +// simple and slow helper conversion to show contents of int grids like a real grid in the ui +// (use eg to quickly display contents of the particle-index grid) + +void debugIntToReal( Grid<int>& source, Grid<Real>& dest, Real factor=1. ) { + FOR_IJK( source ) { dest(i,j,k) = (Real)source(i,j,k) * factor; } +} static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "debugIntToReal" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<int>& source = *_args.getPtr<Grid<int> >("source",0,&_lock); Grid<Real>& dest = *_args.getPtr<Grid<Real> >("dest",1,&_lock); Real factor = _args.getOpt<Real >("factor",2,1. ,&_lock); _retval = getPyNone(); debugIntToReal(source,dest,factor); _args.check(); } pbFinalizePlugin(parent,"debugIntToReal" ); return _retval; } catch(std::exception& e) { pbSetError("debugIntToReal",e.what()); return 0; } } static const Pb::Register _RP_debugIntToReal ("","debugIntToReal",_W_5); + +// build a grid that contains indices for a particle system +// the particles in a cell i,j,k are particles[index(i,j,k)] to particles[index(i+1,j,k)-1] +// (ie, particles[index(i+1,j,k)] alreadu belongs to cell i+1,j,k) + + +void gridParticleIndex( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, FlagGrid& flags, Grid<int>& index, Grid<int>* counter=NULL) { + bool delCounter = false; + if(!counter) { counter = new Grid<int>( flags.getParent() ); delCounter=true; } + else { counter->clear(); } + + // count particles in cells, and delete excess particles + index.clear(); + int inactive = 0; + for (int idx=0; idx<(int)parts.size(); idx++) { + if (parts.isActive(idx)) { + // check index for validity... + Vec3i p = toVec3i( parts.getPos(idx) ); + if (! index.isInBounds(p)) { inactive++; continue; } + + index(p)++; + } else { + inactive++; + } + } + + // note - this one might be smaller... + indexSys.resize( parts.size()-inactive ); + + // convert per cell number to continuous index + int idx=0; + FOR_IJK( index ) { + int num = index(i,j,k); + index(i,j,k) = idx; + idx += num; + } + + // add particles to indexed array, we still need a per cell particle counter + for (int idx=0; idx<(int)parts.size(); idx++) { + if (!parts.isActive(idx)) continue; + Vec3i p = toVec3i( parts.getPos(idx) ); + if (! index.isInBounds(p)) { continue; } + + // initialize position and index into original array + //indexSys[ index(p)+(*counter)(p) ].pos = parts[idx].pos; + indexSys[ index(p)+(*counter)(p) ].sourceIndex = idx; + (*counter)(p)++; + } + + if(delCounter) delete counter; +} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "gridParticleIndex" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleIndexSystem& indexSys = *_args.getPtr<ParticleIndexSystem >("indexSys",1,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",2,&_lock); Grid<int>& index = *_args.getPtr<Grid<int> >("index",3,&_lock); Grid<int>* counter = _args.getPtrOpt<Grid<int> >("counter",4,NULL,&_lock); _retval = getPyNone(); gridParticleIndex(parts,indexSys,flags,index,counter); _args.check(); } pbFinalizePlugin(parent,"gridParticleIndex" ); return _retval; } catch(std::exception& e) { pbSetError("gridParticleIndex",e.what()); return 0; } } static const Pb::Register _RP_gridParticleIndex ("","gridParticleIndex",_W_6); + + + + + struct ComputeUnionLevelsetPindex : public KernelBase { ComputeUnionLevelsetPindex(Grid<int>& index, BasicParticleSystem& parts, ParticleIndexSystem& indexSys, LevelsetGrid& phi, Real radius=1.) : KernelBase(&index,0) ,index(index),parts(parts),indexSys(indexSys),phi(phi),radius(radius) { run(); } inline void op(int i, int j, int k, Grid<int>& index, BasicParticleSystem& parts, ParticleIndexSystem& indexSys, LevelsetGrid& phi, Real radius=1. ) { + const Vec3 gridPos = Vec3(i,j,k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.732; // outside + + int r = int(radius) + 1; + int rZ = phi.is3D() ? r : 0; + for(int zj=k-rZ; zj<=k+rZ; zj++) + for(int yj=j-r ; yj<=j+r ; yj++) + for(int xj=i-r ; xj<=i+r ; xj++) { + if (!phi.isInBounds(Vec3i(xj,yj,zj))) continue; + + // note, for the particle indices in indexSys the access is periodic (ie, dont skip for eg inBounds(sx,10,10) + int isysIdxS = phi.index(xj,yj,zj); + int pStart = index(isysIdxS), pEnd=0; + if(phi.isInBounds(isysIdxS+1)) pEnd = index(isysIdxS+1); + else pEnd = indexSys.size(); + + // now loop over particles in cell + for(int p=pStart; p<pEnd; ++p) { + const int psrc = indexSys[p].sourceIndex; + const Vec3 pos = parts[psrc].pos; + phiv = std::min( phiv , fabs( norm(gridPos-pos) )-radius ); + } + } + phi(i,j,k) = phiv; +} inline Grid<int>& getArg0() { return index; } typedef Grid<int> type0;inline BasicParticleSystem& getArg1() { return parts; } typedef BasicParticleSystem type1;inline ParticleIndexSystem& getArg2() { return indexSys; } typedef ParticleIndexSystem type2;inline LevelsetGrid& getArg3() { return phi; } typedef LevelsetGrid type3;inline Real& getArg4() { return radius; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, index,parts,indexSys,phi,radius); } Grid<int>& index; BasicParticleSystem& parts; ParticleIndexSystem& indexSys; LevelsetGrid& phi; Real radius; }; + + + +void unionParticleLevelset( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, FlagGrid& flags, Grid<int>& index, LevelsetGrid& phi, Real radiusFactor=1. ) { + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + // no reset of phi necessary here + ComputeUnionLevelsetPindex(index, parts, indexSys, phi, radius); +} static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "unionParticleLevelset" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleIndexSystem& indexSys = *_args.getPtr<ParticleIndexSystem >("indexSys",1,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",2,&_lock); Grid<int>& index = *_args.getPtr<Grid<int> >("index",3,&_lock); LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",4,&_lock); Real radiusFactor = _args.getOpt<Real >("radiusFactor",5,1. ,&_lock); _retval = getPyNone(); unionParticleLevelset(parts,indexSys,flags,index,phi,radiusFactor); _args.check(); } pbFinalizePlugin(parent,"unionParticleLevelset" ); return _retval; } catch(std::exception& e) { pbSetError("unionParticleLevelset",e.what()); return 0; } } static const Pb::Register _RP_unionParticleLevelset ("","unionParticleLevelset",_W_7); + + + + + + + struct ComputeAveragedLevelsetWeight : public KernelBase { ComputeAveragedLevelsetWeight(BasicParticleSystem& parts, Grid<int>& index, ParticleIndexSystem& indexSys, LevelsetGrid& phi, Real radius=1.) : KernelBase(&index,0) ,parts(parts),index(index),indexSys(indexSys),phi(phi),radius(radius) { run(); } inline void op(int i, int j, int k, BasicParticleSystem& parts, Grid<int>& index, ParticleIndexSystem& indexSys, LevelsetGrid& phi, Real radius=1. ) { + const Vec3 gridPos = Vec3(i,j,k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.732; // outside + + // loop over neighborhood, similar to ComputeUnionLevelsetPindex + const Real sradiusInv = 1. / (4. * radius * radius) ; + int r = int(1. * radius) + 1; + int rZ = phi.is3D() ? r : 0; + // accumulators + Real wacc = 0.; + Vec3 pacc = Vec3(0.); + Real racc = 0.; + + for(int zj=k-rZ; zj<=k+rZ; zj++) + for(int yj=j-r ; yj<=j+r ; yj++) + for(int xj=i-r ; xj<=i+r ; xj++) { + if (! phi.isInBounds(Vec3i(xj,yj,zj)) ) continue; + + int isysIdxS = phi.index(xj,yj,zj); + int pStart = index(isysIdxS), pEnd=0; + if(phi.isInBounds(isysIdxS+1)) pEnd = index(isysIdxS+1); + else pEnd = indexSys.size(); + for(int p=pStart; p<pEnd; ++p) { + int psrc = indexSys[p].sourceIndex; + Vec3 pos = parts[psrc].pos; + Real s = normSquare(gridPos-pos) * sradiusInv; + Real w = std::max(0., cubed(1.-s) ); + wacc += w; + racc += radius * w; + pacc += pos * w; + } + } + + if(wacc > VECTOR_EPSILON) { + racc /= wacc; + pacc /= wacc; + phiv = fabs( norm(gridPos-pacc) )-racc; + } + phi(i,j,k) = phiv; +} inline BasicParticleSystem& getArg0() { return parts; } typedef BasicParticleSystem type0;inline Grid<int>& getArg1() { return index; } typedef Grid<int> type1;inline ParticleIndexSystem& getArg2() { return indexSys; } typedef ParticleIndexSystem type2;inline LevelsetGrid& getArg3() { return phi; } typedef LevelsetGrid type3;inline Real& getArg4() { return radius; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, parts,index,indexSys,phi,radius); } BasicParticleSystem& parts; Grid<int>& index; ParticleIndexSystem& indexSys; LevelsetGrid& phi; Real radius; }; + +// smoothing, and + +template <class T> struct knSmoothGrid : public KernelBase { knSmoothGrid(Grid<T>& me, Grid<T>& tmp, Real factor) : KernelBase(&me,1) ,me(me),tmp(tmp),factor(factor) { run(); } inline void op(int i, int j, int k, Grid<T>& me, Grid<T>& tmp, Real factor ) { + T val = me(i,j,k) + + me(i+1,j,k) + me(i-1,j,k) + + me(i,j+1,k) + me(i,j-1,k) ; + if(me.is3D()) { + val += me(i,j,k+1) + me(i,j,k-1); + } + tmp(i,j,k) = val * factor; +} inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline Grid<T>& getArg1() { return tmp; } typedef Grid<T> type1;inline Real& getArg2() { return factor; } typedef Real type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, me,tmp,factor); } Grid<T>& me; Grid<T>& tmp; Real factor; }; + + +template <class T> struct knSmoothGridNeg : public KernelBase { knSmoothGridNeg(Grid<T>& me, Grid<T>& tmp, Real factor) : KernelBase(&me,1) ,me(me),tmp(tmp),factor(factor) { run(); } inline void op(int i, int j, int k, Grid<T>& me, Grid<T>& tmp, Real factor ) { + T val = me(i,j,k) + + me(i+1,j,k) + me(i-1,j,k) + + me(i,j+1,k) + me(i,j-1,k) ; + if(me.is3D()) { + val += me(i,j,k+1) + me(i,j,k-1); + } + val *= factor; + if(val<tmp(i,j,k)) tmp(i,j,k) = val; + else tmp(i,j,k) = me(i,j,k); +} inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline Grid<T>& getArg1() { return tmp; } typedef Grid<T> type1;inline Real& getArg2() { return factor; } typedef Real type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, me,tmp,factor); } Grid<T>& me; Grid<T>& tmp; Real factor; }; + + + + + +void averagedParticleLevelset( BasicParticleSystem& parts, ParticleIndexSystem& indexSys, FlagGrid& flags, Grid<int>& index, LevelsetGrid& phi, Real radiusFactor=1. , int smoothen=1 , int smoothenNeg=1 ) { + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + ComputeAveragedLevelsetWeight(parts, index, indexSys, phi, radius); + + // post-process level-set + for(int i=0; i<smoothen; ++i) { + LevelsetGrid tmp(flags.getParent()); + knSmoothGrid<Real>(phi,tmp, 1./(phi.is3D() ? 7. : 5.) ); + phi.swap(tmp); + } + for(int i=0; i<smoothenNeg; ++i) { + LevelsetGrid tmp(flags.getParent()); + knSmoothGridNeg<Real>(phi,tmp, 1./(phi.is3D() ? 7. : 5.) ); + phi.swap(tmp); + } + // NT_DEBUG , todo copy border +} static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "averagedParticleLevelset" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleIndexSystem& indexSys = *_args.getPtr<ParticleIndexSystem >("indexSys",1,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",2,&_lock); Grid<int>& index = *_args.getPtr<Grid<int> >("index",3,&_lock); LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",4,&_lock); Real radiusFactor = _args.getOpt<Real >("radiusFactor",5,1. ,&_lock); int smoothen = _args.getOpt<int >("smoothen",6,1 ,&_lock); int smoothenNeg = _args.getOpt<int >("smoothenNeg",7,1 ,&_lock); _retval = getPyNone(); averagedParticleLevelset(parts,indexSys,flags,index,phi,radiusFactor,smoothen,smoothenNeg); _args.check(); } pbFinalizePlugin(parent,"averagedParticleLevelset" ); return _retval; } catch(std::exception& e) { pbSetError("averagedParticleLevelset",e.what()); return 0; } } static const Pb::Register _RP_averagedParticleLevelset ("","averagedParticleLevelset",_W_8); + + + + +//****************************************************************************** +// grid interpolation functions + + +template <class T> struct knSafeDivReal : public KernelBase { knSafeDivReal(Grid<T>& me, const Grid<Real>& other, Real cutoff=VECTOR_EPSILON) : KernelBase(&me,0) ,me(me),other(other),cutoff(cutoff) { run(); } inline void op(int idx, Grid<T>& me, const Grid<Real>& other, Real cutoff=VECTOR_EPSILON ) { + if(other[idx]<cutoff) { + me[idx] = 0.; + } else { + T div( other[idx] ); + me[idx] = safeDivide(me[idx], div ); + } +} inline Grid<T>& getArg0() { return me; } typedef Grid<T> type0;inline const Grid<Real>& getArg1() { return other; } typedef Grid<Real> type1;inline Real& getArg2() { return cutoff; } typedef Real type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, me,other,cutoff); } Grid<T>& me; const Grid<Real>& other; Real cutoff; }; + +// Set velocities on the grid from the particle system + + + struct knStompVec3PerComponent : public KernelBase { knStompVec3PerComponent(Grid<Vec3>& grid, Real threshold) : KernelBase(&grid,0) ,grid(grid),threshold(threshold) { run(); } inline void op(int idx, Grid<Vec3>& grid, Real threshold ) { + if(grid[idx][0] < threshold) grid[idx][0] = 0.; + if(grid[idx][1] < threshold) grid[idx][1] = 0.; + if(grid[idx][2] < threshold) grid[idx][2] = 0.; +} inline Grid<Vec3>& getArg0() { return grid; } typedef Grid<Vec3> type0;inline Real& getArg1() { return threshold; } typedef Real type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, grid,threshold); } Grid<Vec3>& grid; Real threshold; }; + + + + + struct knMapLinearVec3ToMACGrid : public KernelBase { knMapLinearVec3ToMACGrid( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, Grid<Vec3>& tmp, ParticleDataImpl<Vec3>& pvel ) : KernelBase(p.size()) ,p(p),flags(flags),vel(vel),tmp(tmp),pvel(pvel) { run(); } inline void op(int idx, BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, Grid<Vec3>& tmp, ParticleDataImpl<Vec3>& pvel ) { + unusedParameter(flags); + if (!p.isActive(idx)) return; + vel.setInterpolated( p[idx].pos, pvel[idx], &tmp[0] ); +} inline BasicParticleSystem& getArg0() { return p; } typedef BasicParticleSystem type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline Grid<Vec3>& getArg3() { return tmp; } typedef Grid<Vec3> type3;inline ParticleDataImpl<Vec3>& getArg4() { return pvel; } typedef ParticleDataImpl<Vec3> type4; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,vel,tmp,pvel); } BasicParticleSystem& p; FlagGrid& flags; MACGrid& vel; Grid<Vec3>& tmp; ParticleDataImpl<Vec3>& pvel; }; + +// optionally , this function can use an existing vec3 grid to store the weights +// this is useful in combination with the simple extrapolation function + + +void mapPartsToMAC( FlagGrid& flags, MACGrid& vel , MACGrid& velOld , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel , Grid<Vec3>* weight=NULL ) { + // interpol -> grid. tmpgrid for particle contribution weights + bool freeTmp = false; + if(!weight) { + weight = new Grid<Vec3>(flags.getParent()); + freeTmp = true; + } else { + weight->clear(); // make sure we start with a zero grid! + } + vel.clear(); + knMapLinearVec3ToMACGrid( parts, flags, vel, *weight, partVel ); + + // stomp small values in weight to zero to prevent roundoff errors + knStompVec3PerComponent( *weight, VECTOR_EPSILON ); + vel.safeDivide(*weight); + + // store original state + velOld.copyFrom( vel ); + if(freeTmp) delete weight; +} static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapPartsToMAC" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); MACGrid& velOld = *_args.getPtr<MACGrid >("velOld",2,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",3,&_lock); ParticleDataImpl<Vec3>& partVel = *_args.getPtr<ParticleDataImpl<Vec3> >("partVel",4,&_lock); Grid<Vec3>* weight = _args.getPtrOpt<Grid<Vec3> >("weight",5,NULL ,&_lock); _retval = getPyNone(); mapPartsToMAC(flags,vel,velOld,parts,partVel,weight); _args.check(); } pbFinalizePlugin(parent,"mapPartsToMAC" ); return _retval; } catch(std::exception& e) { pbSetError("mapPartsToMAC",e.what()); return 0; } } static const Pb::Register _RP_mapPartsToMAC ("","mapPartsToMAC",_W_9); + + + + + +template <class T> struct knMapLinear : public KernelBase { knMapLinear( BasicParticleSystem& p, FlagGrid& flags, Grid<T>& target, Grid<Real>& gtmp, ParticleDataImpl<T>& psource ) : KernelBase(p.size()) ,p(p),flags(flags),target(target),gtmp(gtmp),psource(psource) { run(); } inline void op(int idx, BasicParticleSystem& p, FlagGrid& flags, Grid<T>& target, Grid<Real>& gtmp, ParticleDataImpl<T>& psource ) { + unusedParameter(flags); + if (!p.isActive(idx)) return; + target.setInterpolated( p[idx].pos, psource[idx], gtmp ); +} inline BasicParticleSystem& getArg0() { return p; } typedef BasicParticleSystem type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline Grid<T>& getArg2() { return target; } typedef Grid<T> type2;inline Grid<Real>& getArg3() { return gtmp; } typedef Grid<Real> type3;inline ParticleDataImpl<T>& getArg4() { return psource; } typedef ParticleDataImpl<T> type4; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,target,gtmp,psource); } BasicParticleSystem& p; FlagGrid& flags; Grid<T>& target; Grid<Real>& gtmp; ParticleDataImpl<T>& psource; }; +template<class T> +void mapLinearRealHelper( FlagGrid& flags, Grid<T>& target , + BasicParticleSystem& parts , ParticleDataImpl<T>& source ) +{ + Grid<Real> tmp(flags.getParent()); + target.clear(); + knMapLinear<T>( parts, flags, target, tmp, source ); + knSafeDivReal<T>( target, tmp ); +} + +void mapPartsToGrid( FlagGrid& flags, Grid<Real>& target , BasicParticleSystem& parts , ParticleDataImpl<Real>& source ) { + mapLinearRealHelper<Real>(flags,target,parts,source); +} static PyObject* _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapPartsToGrid" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& target = *_args.getPtr<Grid<Real> >("target",1,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",2,&_lock); ParticleDataImpl<Real>& source = *_args.getPtr<ParticleDataImpl<Real> >("source",3,&_lock); _retval = getPyNone(); mapPartsToGrid(flags,target,parts,source); _args.check(); } pbFinalizePlugin(parent,"mapPartsToGrid" ); return _retval; } catch(std::exception& e) { pbSetError("mapPartsToGrid",e.what()); return 0; } } static const Pb::Register _RP_mapPartsToGrid ("","mapPartsToGrid",_W_10); +void mapPartsToGridVec3( FlagGrid& flags, Grid<Vec3>& target , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& source ) { + mapLinearRealHelper<Vec3>(flags,target,parts,source); +} static PyObject* _W_11 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapPartsToGridVec3" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Vec3>& target = *_args.getPtr<Grid<Vec3> >("target",1,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",2,&_lock); ParticleDataImpl<Vec3>& source = *_args.getPtr<ParticleDataImpl<Vec3> >("source",3,&_lock); _retval = getPyNone(); mapPartsToGridVec3(flags,target,parts,source); _args.check(); } pbFinalizePlugin(parent,"mapPartsToGridVec3" ); return _retval; } catch(std::exception& e) { pbSetError("mapPartsToGridVec3",e.what()); return 0; } } static const Pb::Register _RP_mapPartsToGridVec3 ("","mapPartsToGridVec3",_W_11); +// integers need "max" mode, not yet implemented +//PYTHON void mapPartsToGridInt ( FlagGrid& flags, Grid<int >& target , BasicParticleSystem& parts , ParticleDataImpl<int >& source ) { +// mapLinearRealHelper<int >(flags,target,parts,source); +//} + + + +template <class T> struct knMapFromGrid : public KernelBase { knMapFromGrid( BasicParticleSystem& p, Grid<T>& gsrc, ParticleDataImpl<T>& target ) : KernelBase(p.size()) ,p(p),gsrc(gsrc),target(target) { run(); } inline void op(int idx, BasicParticleSystem& p, Grid<T>& gsrc, ParticleDataImpl<T>& target ) { + if (!p.isActive(idx)) return; + target[idx] = gsrc.getInterpolated( p[idx].pos ); +} inline BasicParticleSystem& getArg0() { return p; } typedef BasicParticleSystem type0;inline Grid<T>& getArg1() { return gsrc; } typedef Grid<T> type1;inline ParticleDataImpl<T>& getArg2() { return target; } typedef ParticleDataImpl<T> type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,gsrc,target); } BasicParticleSystem& p; Grid<T>& gsrc; ParticleDataImpl<T>& target; }; +void mapGridToParts( Grid<Real>& source , BasicParticleSystem& parts , ParticleDataImpl<Real>& target ) { + knMapFromGrid<Real>(parts, source, target); +} static PyObject* _W_12 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapGridToParts" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& source = *_args.getPtr<Grid<Real> >("source",0,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",1,&_lock); ParticleDataImpl<Real>& target = *_args.getPtr<ParticleDataImpl<Real> >("target",2,&_lock); _retval = getPyNone(); mapGridToParts(source,parts,target); _args.check(); } pbFinalizePlugin(parent,"mapGridToParts" ); return _retval; } catch(std::exception& e) { pbSetError("mapGridToParts",e.what()); return 0; } } static const Pb::Register _RP_mapGridToParts ("","mapGridToParts",_W_12); +void mapGridToPartsVec3( Grid<Vec3>& source , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& target ) { + knMapFromGrid<Vec3>(parts, source, target); +} static PyObject* _W_13 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapGridToPartsVec3" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Vec3>& source = *_args.getPtr<Grid<Vec3> >("source",0,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",1,&_lock); ParticleDataImpl<Vec3>& target = *_args.getPtr<ParticleDataImpl<Vec3> >("target",2,&_lock); _retval = getPyNone(); mapGridToPartsVec3(source,parts,target); _args.check(); } pbFinalizePlugin(parent,"mapGridToPartsVec3" ); return _retval; } catch(std::exception& e) { pbSetError("mapGridToPartsVec3",e.what()); return 0; } } static const Pb::Register _RP_mapGridToPartsVec3 ("","mapGridToPartsVec3",_W_13); + + +// Get velocities from grid + + + + struct knMapLinearMACGridToVec3_PIC : public KernelBase { knMapLinearMACGridToVec3_PIC( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, ParticleDataImpl<Vec3>& pvel ) : KernelBase(p.size()) ,p(p),flags(flags),vel(vel),pvel(pvel) { run(); } inline void op(int idx, BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, ParticleDataImpl<Vec3>& pvel ) { + if (!p.isActive(idx)) return; + // pure PIC + pvel[idx] = vel.getInterpolated( p[idx].pos ); +} inline BasicParticleSystem& getArg0() { return p; } typedef BasicParticleSystem type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline ParticleDataImpl<Vec3>& getArg3() { return pvel; } typedef ParticleDataImpl<Vec3> type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,vel,pvel); } BasicParticleSystem& p; FlagGrid& flags; MACGrid& vel; ParticleDataImpl<Vec3>& pvel; }; + +void mapMACToParts(FlagGrid& flags, MACGrid& vel , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel ) { + knMapLinearMACGridToVec3_PIC( parts, flags, vel, partVel ); +} static PyObject* _W_14 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "mapMACToParts" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",2,&_lock); ParticleDataImpl<Vec3>& partVel = *_args.getPtr<ParticleDataImpl<Vec3> >("partVel",3,&_lock); _retval = getPyNone(); mapMACToParts(flags,vel,parts,partVel); _args.check(); } pbFinalizePlugin(parent,"mapMACToParts" ); return _retval; } catch(std::exception& e) { pbSetError("mapMACToParts",e.what()); return 0; } } static const Pb::Register _RP_mapMACToParts ("","mapMACToParts",_W_14); + +// with flip delta interpolation + + + struct knMapLinearMACGridToVec3_FLIP : public KernelBase { knMapLinearMACGridToVec3_FLIP( BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, MACGrid& oldVel, ParticleDataImpl<Vec3>& pvel , Real flipRatio) : KernelBase(p.size()) ,p(p),flags(flags),vel(vel),oldVel(oldVel),pvel(pvel),flipRatio(flipRatio) { run(); } inline void op(int idx, BasicParticleSystem& p, FlagGrid& flags, MACGrid& vel, MACGrid& oldVel, ParticleDataImpl<Vec3>& pvel , Real flipRatio ) { + if (!p.isActive(idx)) return; + Vec3 v = vel.getInterpolated(p[idx].pos); + Vec3 delta = v - oldVel.getInterpolated(p[idx].pos); + pvel[idx] = flipRatio * (pvel[idx] + delta) + (1.0 - flipRatio) * v; +} inline BasicParticleSystem& getArg0() { return p; } typedef BasicParticleSystem type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline MACGrid& getArg3() { return oldVel; } typedef MACGrid type3;inline ParticleDataImpl<Vec3>& getArg4() { return pvel; } typedef ParticleDataImpl<Vec3> type4;inline Real& getArg5() { return flipRatio; } typedef Real type5; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,vel,oldVel,pvel,flipRatio); } BasicParticleSystem& p; FlagGrid& flags; MACGrid& vel; MACGrid& oldVel; ParticleDataImpl<Vec3>& pvel; Real flipRatio; }; + + +void flipVelocityUpdate(FlagGrid& flags, MACGrid& vel , MACGrid& velOld , BasicParticleSystem& parts , ParticleDataImpl<Vec3>& partVel , Real flipRatio ) { + knMapLinearMACGridToVec3_FLIP( parts, flags, vel, velOld, partVel, flipRatio ); +} static PyObject* _W_15 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "flipVelocityUpdate" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); MACGrid& velOld = *_args.getPtr<MACGrid >("velOld",2,&_lock); BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",3,&_lock); ParticleDataImpl<Vec3>& partVel = *_args.getPtr<ParticleDataImpl<Vec3> >("partVel",4,&_lock); Real flipRatio = _args.get<Real >("flipRatio",5,&_lock); _retval = getPyNone(); flipVelocityUpdate(flags,vel,velOld,parts,partVel,flipRatio); _args.check(); } pbFinalizePlugin(parent,"flipVelocityUpdate" ); return _retval; } catch(std::exception& e) { pbSetError("flipVelocityUpdate",e.what()); return 0; } } static const Pb::Register _RP_flipVelocityUpdate ("","flipVelocityUpdate",_W_15); + + +} // namespace + + + diff --git a/source/blender/python/manta_pp/plugin/initplugins.cpp b/source/blender/python/manta_pp/plugin/initplugins.cpp new file mode 100644 index 00000000000..6d2c0d2f160 --- /dev/null +++ b/source/blender/python/manta_pp/plugin/initplugins.cpp @@ -0,0 +1,201 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Tools to setup fields and inflows + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "particle.h" +#include "noisefield.h" +#include "mesh.h" + +using namespace std; + +namespace Manta { + +//! Apply noise to grid + + + struct KnApplyNoise : public KernelBase { KnApplyNoise(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Grid<Real>& sdf, Real scale, Real sigma) : KernelBase(&flags,0) ,flags(flags),density(density),noise(noise),sdf(sdf),scale(scale),sigma(sigma) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Grid<Real>& sdf, Real scale, Real sigma ) { + if (!flags.isFluid(i,j,k) || sdf(i,j,k) > sigma) return; + Real factor = clamp(1.0-0.5/sigma * (sdf(i,j,k)+sigma), 0.0, 1.0); + + Real target = noise.evaluate(Vec3(i,j,k)) * scale * factor; + if (density(i,j,k) < target) + density(i,j,k) = target; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return density; } typedef Grid<Real> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Grid<Real>& getArg3() { return sdf; } typedef Grid<Real> type3;inline Real& getArg4() { return scale; } typedef Real type4;inline Real& getArg5() { return sigma; } typedef Real type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,density,noise,sdf,scale,sigma); } FlagGrid& flags; Grid<Real>& density; WaveletNoiseField& noise; Grid<Real>& sdf; Real scale; Real sigma; }; + + + + struct KnApplyDensity : public KernelBase { KnApplyDensity(FlagGrid& flags, Grid<Real>& density, Grid<Real>& sdf, Real value, Real sigma) : KernelBase(&flags,0) ,flags(flags),density(density),sdf(sdf),value(value),sigma(sigma) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& density, Grid<Real>& sdf, Real value, Real sigma ) { + if (!flags.isFluid(i,j,k) || sdf(i,j,k) > sigma) return; + density(i,j,k) = value; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return density; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return sdf; } typedef Grid<Real> type2;inline Real& getArg3() { return value; } typedef Real type3;inline Real& getArg4() { return sigma; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,density,sdf,value,sigma); } FlagGrid& flags; Grid<Real>& density; Grid<Real>& sdf; Real value; Real sigma; }; + + + + struct KnApplyEmission : public KernelBase { KnApplyEmission(FlagGrid& flags, Grid<Real>& density, Grid<Real>& emission, bool isAbsolute) : KernelBase(&flags,0) ,flags(flags),density(density),emission(emission),isAbsolute(isAbsolute) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& density, Grid<Real>& emission, bool isAbsolute ) { + if (!flags.isFluid(i,j,k) || emission(i,j,k) == 0.) return; + if (isAbsolute) + density(i,j,k) = emission(i,j,k); + else + density(i,j,k) += emission(i,j,k); +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return density; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return emission; } typedef Grid<Real> type2;inline bool& getArg3() { return isAbsolute; } typedef bool type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,density,emission,isAbsolute); } FlagGrid& flags; Grid<Real>& density; Grid<Real>& emission; bool isAbsolute; }; + +//! Init noise-modulated density inside shape + +void densityInflow(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Shape* shape, Real scale=1.0, Real sigma=0) { + Grid<Real> sdf = shape->computeLevelset(); + KnApplyNoise(flags, density, noise, sdf, scale, sigma); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "densityInflow" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Shape* shape = _args.getPtr<Shape >("shape",3,&_lock); Real scale = _args.getOpt<Real >("scale",4,1.0,&_lock); Real sigma = _args.getOpt<Real >("sigma",5,0,&_lock); _retval = getPyNone(); densityInflow(flags,density,noise,shape,scale,sigma); _args.check(); } pbFinalizePlugin(parent,"densityInflow" ); return _retval; } catch(std::exception& e) { pbSetError("densityInflow",e.what()); return 0; } } static const Pb::Register _RP_densityInflow ("","densityInflow",_W_0); + + +//! Init noise-modulated density inside mesh + +void densityInflowMeshNoise(FlagGrid& flags, Grid<Real>& density, WaveletNoiseField& noise, Mesh* mesh, Real scale=1.0, Real sigma=0) { + FluidSolver dummy(density.getSize()); + LevelsetGrid sdf(&dummy, false); + mesh->meshSDF(*mesh, sdf, 1.); + KnApplyNoise(flags, density, noise, sdf, scale, sigma); +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "densityInflowMeshNoise" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Mesh* mesh = _args.getPtr<Mesh >("mesh",3,&_lock); Real scale = _args.getOpt<Real >("scale",4,1.0,&_lock); Real sigma = _args.getOpt<Real >("sigma",5,0,&_lock); _retval = getPyNone(); densityInflowMeshNoise(flags,density,noise,mesh,scale,sigma); _args.check(); } pbFinalizePlugin(parent,"densityInflowMeshNoise" ); return _retval; } catch(std::exception& e) { pbSetError("densityInflowMeshNoise",e.what()); return 0; } } static const Pb::Register _RP_densityInflowMeshNoise ("","densityInflowMeshNoise",_W_1); +//! Init still density inside mesh + +void densityInflowMesh(FlagGrid& flags, Grid<Real>& density, Mesh* mesh, Real value=1., Real cutoff = 7, Real sigma=0) { + FluidSolver dummy(density.getSize()); + LevelsetGrid sdf(&dummy, false); + mesh->meshSDF(*mesh, sdf, 2.,cutoff); + KnApplyDensity(flags, density, sdf, value, sigma); +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "densityInflowMesh" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); Mesh* mesh = _args.getPtr<Mesh >("mesh",2,&_lock); Real value = _args.getOpt<Real >("value",3,1.,&_lock); Real cutoff = _args.getOpt<Real >("cutoff",4,7,&_lock); Real sigma = _args.getOpt<Real >("sigma",5,0,&_lock); _retval = getPyNone(); densityInflowMesh(flags,density,mesh,value,cutoff,sigma); _args.check(); } pbFinalizePlugin(parent,"densityInflowMesh" ); return _retval; } catch(std::exception& e) { pbSetError("densityInflowMesh",e.what()); return 0; } } static const Pb::Register _RP_densityInflowMesh ("","densityInflowMesh",_W_2); +//! Add emission values +//isAbsolute: whether to add emission values to existing, or replace + +void applyEmission(FlagGrid& flags, Grid<Real>& density, Grid<Real>& emission, bool isAbsolute) { + FluidSolver dummy(density.getSize()); + KnApplyEmission(flags, density, emission, isAbsolute); +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "applyEmission" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); Grid<Real>& emission = *_args.getPtr<Grid<Real> >("emission",2,&_lock); bool isAbsolute = _args.get<bool >("isAbsolute",3,&_lock); _retval = getPyNone(); applyEmission(flags,density,emission,isAbsolute); _args.check(); } pbFinalizePlugin(parent,"applyEmission" ); return _retval; } catch(std::exception& e) { pbSetError("applyEmission",e.what()); return 0; } } static const Pb::Register _RP_applyEmission ("","applyEmission",_W_3); +//! sample noise field and set pdata with its values (for convenience, scale the noise values) + +template <class T> struct knSetPdataNoise : public KernelBase { knSetPdataNoise(BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale) : KernelBase(parts.size()) ,parts(parts),pdata(pdata),noise(noise),scale(scale) { run(); } inline void op(int idx, BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale ) { + pdata[idx] = noise.evaluate( parts.getPos(idx) ) * scale; +} inline BasicParticleSystem& getArg0() { return parts; } typedef BasicParticleSystem type0;inline ParticleDataImpl<T>& getArg1() { return pdata; } typedef ParticleDataImpl<T> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Real& getArg3() { return scale; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, parts,pdata,noise,scale); } BasicParticleSystem& parts; ParticleDataImpl<T>& pdata; WaveletNoiseField& noise; Real scale; }; + +template <class T> struct knSetPdataNoiseVec : public KernelBase { knSetPdataNoiseVec(BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale) : KernelBase(parts.size()) ,parts(parts),pdata(pdata),noise(noise),scale(scale) { run(); } inline void op(int idx, BasicParticleSystem& parts, ParticleDataImpl<T>& pdata, WaveletNoiseField& noise, Real scale ) { + pdata[idx] = noise.evaluateVec( parts.getPos(idx) ) * scale; +} inline BasicParticleSystem& getArg0() { return parts; } typedef BasicParticleSystem type0;inline ParticleDataImpl<T>& getArg1() { return pdata; } typedef ParticleDataImpl<T> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Real& getArg3() { return scale; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, parts,pdata,noise,scale); } BasicParticleSystem& parts; ParticleDataImpl<T>& pdata; WaveletNoiseField& noise; Real scale; }; +void setNoisePdata(BasicParticleSystem& parts, ParticleDataImpl<Real>& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoise<Real>(parts, pd,noise,scale); } static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setNoisePdata" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleDataImpl<Real>& pd = *_args.getPtr<ParticleDataImpl<Real> >("pd",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.,&_lock); _retval = getPyNone(); setNoisePdata(parts,pd,noise,scale); _args.check(); } pbFinalizePlugin(parent,"setNoisePdata" ); return _retval; } catch(std::exception& e) { pbSetError("setNoisePdata",e.what()); return 0; } } static const Pb::Register _RP_setNoisePdata ("","setNoisePdata",_W_4); +void setNoisePdataVec3(BasicParticleSystem& parts, ParticleDataImpl<Vec3>& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoiseVec<Vec3>(parts, pd,noise,scale); } static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setNoisePdataVec3" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleDataImpl<Vec3>& pd = *_args.getPtr<ParticleDataImpl<Vec3> >("pd",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.,&_lock); _retval = getPyNone(); setNoisePdataVec3(parts,pd,noise,scale); _args.check(); } pbFinalizePlugin(parent,"setNoisePdataVec3" ); return _retval; } catch(std::exception& e) { pbSetError("setNoisePdataVec3",e.what()); return 0; } } static const Pb::Register _RP_setNoisePdataVec3 ("","setNoisePdataVec3",_W_5); +void setNoisePdataInt(BasicParticleSystem& parts, ParticleDataImpl<int >& pd, WaveletNoiseField& noise, Real scale=1.) { knSetPdataNoise<int> (parts, pd,noise,scale); } static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setNoisePdataInt" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); ParticleDataImpl<int >& pd = *_args.getPtr<ParticleDataImpl<int > >("pd",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.,&_lock); _retval = getPyNone(); setNoisePdataInt(parts,pd,noise,scale); _args.check(); } pbFinalizePlugin(parent,"setNoisePdataInt" ); return _retval; } catch(std::exception& e) { pbSetError("setNoisePdataInt",e.what()); return 0; } } static const Pb::Register _RP_setNoisePdataInt ("","setNoisePdataInt",_W_6); + +//! SDF gradient from obstacle flags +Grid<Vec3> obstacleGradient(FlagGrid& flags) { + LevelsetGrid levelset(flags.getParent(),false); + Grid<Vec3> gradient(flags.getParent()); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + // build levelset gradient + GradientOp(gradient, levelset); + + FOR_IDX(levelset) { + Vec3 grad = gradient[idx]; + Real s = normalize(grad); + if (s <= 0.1 || levelset[idx] >= 0) + grad=Vec3(0.); + gradient[idx] = grad * levelset[idx]; + } + + return gradient; +} static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "obstacleGradient" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); _retval = toPy(obstacleGradient(flags)); _args.check(); } pbFinalizePlugin(parent,"obstacleGradient" ); return _retval; } catch(std::exception& e) { pbSetError("obstacleGradient",e.what()); return 0; } } static const Pb::Register _RP_obstacleGradient ("","obstacleGradient",_W_7); + +LevelsetGrid obstacleLevelset(FlagGrid& flags) { + LevelsetGrid levelset(flags.getParent(),false); + Grid<Vec3> gradient(flags.getParent()); + + // rebuild obstacle levelset + FOR_IDX(levelset) { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + return levelset; +} static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "obstacleLevelset" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); _retval = toPy(obstacleLevelset(flags)); _args.check(); } pbFinalizePlugin(parent,"obstacleLevelset" ); return _retval; } catch(std::exception& e) { pbSetError("obstacleLevelset",e.what()); return 0; } } static const Pb::Register _RP_obstacleLevelset ("","obstacleLevelset",_W_8); + +// helper functions for pdata operator tests + +//! init some test particles at the origin + +void addTestParts( BasicParticleSystem& parts, int num) { + for(int i=0; i<num; ++i) + parts.addBuffered( Vec3(0,0,0) ); + + parts.doCompress(); + parts.insertBufferedParticles(); +} static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addTestParts" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); int num = _args.get<int >("num",1,&_lock); _retval = getPyNone(); addTestParts(parts,num); _args.check(); } pbFinalizePlugin(parent,"addTestParts" ); return _retval; } catch(std::exception& e) { pbSetError("addTestParts",e.what()); return 0; } } static const Pb::Register _RP_addTestParts ("","addTestParts",_W_9); + +// calculate the difference between two pdata fields (note - slow!, not parallelized) + +Real pdataMaxDiff( ParticleDataBase* a, ParticleDataBase* b ) { + double maxVal = 0.; + //debMsg(" PD "<< a->getType()<<" as"<<a->getSizeSlow()<<" bs"<<b->getSizeSlow() , 1); + assertMsg(a->getType() == b->getType() , "pdataMaxDiff problem - different pdata types!"); + assertMsg(a->getSizeSlow() == b->getSizeSlow(), "pdataMaxDiff problem -different pdata sizes!"); + + if (a->getType() & ParticleDataBase::TypeReal) + { + ParticleDataImpl<Real>& av = *dynamic_cast<ParticleDataImpl<Real>*>(a); + ParticleDataImpl<Real>& bv = *dynamic_cast<ParticleDataImpl<Real>*>(b); + FOR_PARTS(av) { + maxVal = std::max(maxVal, (double)fabs( av[idx]-bv[idx] )); + } + } else if (a->getType() & ParticleDataBase::TypeInt) + { + ParticleDataImpl<int>& av = *dynamic_cast<ParticleDataImpl<int>*>(a); + ParticleDataImpl<int>& bv = *dynamic_cast<ParticleDataImpl<int>*>(b); + FOR_PARTS(av) { + maxVal = std::max(maxVal, (double)fabs( (double)av[idx]-bv[idx] )); + } + } else if (a->getType() & ParticleDataBase::TypeVec3) { + ParticleDataImpl<Vec3>& av = *dynamic_cast<ParticleDataImpl<Vec3>*>(a); + ParticleDataImpl<Vec3>& bv = *dynamic_cast<ParticleDataImpl<Vec3>*>(b); + FOR_PARTS(av) { + double d = 0.; + for(int c=0; c<3; ++c) { + d += fabs( (double)av[idx][c] - (double)bv[idx][c] ); + } + maxVal = std::max(maxVal, d ); + } + } else { + errMsg("pdataMaxDiff: Grid Type is not supported (only Real, Vec3, int)"); + } + + return maxVal; +} static PyObject* _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "pdataMaxDiff" ); PyObject *_retval = 0; { ArgLocker _lock; ParticleDataBase* a = _args.getPtr<ParticleDataBase >("a",0,&_lock); ParticleDataBase* b = _args.getPtr<ParticleDataBase >("b",1,&_lock); _retval = toPy(pdataMaxDiff(a,b)); _args.check(); } pbFinalizePlugin(parent,"pdataMaxDiff" ); return _retval; } catch(std::exception& e) { pbSetError("pdataMaxDiff",e.what()); return 0; } } static const Pb::Register _RP_pdataMaxDiff ("","pdataMaxDiff",_W_10); + +} // namespace + + + diff --git a/source/blender/python/manta_pp/plugin/kepsilon.cpp b/source/blender/python/manta_pp/plugin/kepsilon.cpp new file mode 100644 index 00000000000..bbec1a9d2fb --- /dev/null +++ b/source/blender/python/manta_pp/plugin/kepsilon.cpp @@ -0,0 +1,195 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Turbulence modeling plugins + * + ******************************************************************************/ + +#include "grid.h" +#include "commonkernels.h" +#include "vortexsheet.h" +#include "conjugategrad.h" + +using namespace std; + +namespace Manta { + +// k-epsilon model constants +const Real keCmu = 0.09; +const Real keC1 = 1.44; +const Real keC2 = 1.92; +const Real keS1 = 1.0; +const Real keS2 = 1.3; + +// k-epsilon limiters +const Real keU0 = 1.0; +const Real keImin = 2e-3; +const Real keImax = 1.0; +const Real keNuMin = 1e-3; +const Real keNuMax = 5.0; + +//! clamp k and epsilon to limits + + struct KnTurbulenceClamp : public KernelBase { KnTurbulenceClamp(Grid<Real>& kgrid, Grid<Real>& egrid, Real minK, Real maxK, Real minNu, Real maxNu) : KernelBase(&kgrid,0) ,kgrid(kgrid),egrid(egrid),minK(minK),maxK(maxK),minNu(minNu),maxNu(maxNu) { run(); } inline void op(int idx, Grid<Real>& kgrid, Grid<Real>& egrid, Real minK, Real maxK, Real minNu, Real maxNu ) { + Real eps = egrid[idx]; + Real ke = clamp(kgrid[idx],minK,maxK); + Real nu = keCmu*square(ke)/eps; + if (nu > maxNu) + eps = keCmu*square(ke)/maxNu; + if (nu < minNu) + eps = keCmu*square(ke)/minNu; + + kgrid[idx] = ke; + egrid[idx] = eps; +} inline Grid<Real>& getArg0() { return kgrid; } typedef Grid<Real> type0;inline Grid<Real>& getArg1() { return egrid; } typedef Grid<Real> type1;inline Real& getArg2() { return minK; } typedef Real type2;inline Real& getArg3() { return maxK; } typedef Real type3;inline Real& getArg4() { return minNu; } typedef Real type4;inline Real& getArg5() { return maxNu; } typedef Real type5; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, kgrid,egrid,minK,maxK,minNu,maxNu); } Grid<Real>& kgrid; Grid<Real>& egrid; Real minK; Real maxK; Real minNu; Real maxNu; }; + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity nu_T=C_mu*k^2/eps + + + + struct KnComputeProduction : public KernelBase { KnComputeProduction(const MACGrid& vel, const Grid<Vec3>& velCenter, const Grid<Real>& ke, const Grid<Real>& eps, Grid<Real>& prod, Grid<Real>& nuT, Grid<Real>* strain, Real pscale = 1.0f) : KernelBase(&vel,1) ,vel(vel),velCenter(velCenter),ke(ke),eps(eps),prod(prod),nuT(nuT),strain(strain),pscale(pscale) { run(); } inline void op(int i, int j, int k, const MACGrid& vel, const Grid<Vec3>& velCenter, const Grid<Real>& ke, const Grid<Real>& eps, Grid<Real>& prod, Grid<Real>& nuT, Grid<Real>* strain, Real pscale = 1.0f ) { + Real curEps = eps(i,j,k); + if (curEps > 0) { + // turbulent viscosity: nu_T = C_mu * k^2/eps + Real curNu = keCmu * square(ke(i,j,k)) / curEps; + + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, vel(i,j,k+1).z) - vel(i,j,k); + Vec3 ux = 0.5*(velCenter(i+1,j,k)-velCenter(i-1,j,k)); + Vec3 uy = 0.5*(velCenter(i,j+1,k)-velCenter(i,j-1,k)); + Vec3 uz = 0.5*(velCenter(i,j,k+1)-velCenter(i,j,k-1)); + Real S12 = 0.5*(ux.y+uy.x); + Real S13 = 0.5*(ux.z+uz.x); + Real S23 = 0.5*(uy.z+uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + + 2.0*square(S12) + 2.0*square(S13) + 2.0*square(S23); + + // P = 2*nu_T*sum_ij(Sij^2) + prod(i,j,k) = 2.0 * curNu * S2 * pscale; + nuT(i,j,k) = curNu; + if (strain) (*strain)(i,j,k) = sqrt(S2); + } + else { + prod(i,j,k) = 0; + nuT(i,j,k) = 0; + if (strain) (*strain)(i,j,k) = 0; + } +} inline const MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline const Grid<Vec3>& getArg1() { return velCenter; } typedef Grid<Vec3> type1;inline const Grid<Real>& getArg2() { return ke; } typedef Grid<Real> type2;inline const Grid<Real>& getArg3() { return eps; } typedef Grid<Real> type3;inline Grid<Real>& getArg4() { return prod; } typedef Grid<Real> type4;inline Grid<Real>& getArg5() { return nuT; } typedef Grid<Real> type5;inline Grid<Real>* getArg6() { return strain; } typedef Grid<Real> type6;inline Real& getArg7() { return pscale; } typedef Real type7; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,velCenter,ke,eps,prod,nuT,strain,pscale); } const MACGrid& vel; const Grid<Vec3>& velCenter; const Grid<Real>& ke; const Grid<Real>& eps; Grid<Real>& prod; Grid<Real>& nuT; Grid<Real>* strain; Real pscale; }; + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity nu_T=C_mu*k^2/eps + +void KEpsilonComputeProduction(MACGrid& vel, Grid<Real>& k, Grid<Real>& eps, Grid<Real>& prod, Grid<Real>& nuT, Grid<Real>* strain=0, Real pscale = 1.0f) { + // get centered velocity grid + Grid<Vec3> vcenter(k.getParent()); + GetCentered(vcenter, vel); + FillInBoundary(vcenter,1); + + // compute limits + const Real minK = 1.5*square(keU0)*square(keImin); + const Real maxK = 1.5*square(keU0)*square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); + + KnComputeProduction(vel, vcenter, k, eps, prod, nuT, strain, pscale); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "KEpsilonComputeProduction" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Real>& k = *_args.getPtr<Grid<Real> >("k",1,&_lock); Grid<Real>& eps = *_args.getPtr<Grid<Real> >("eps",2,&_lock); Grid<Real>& prod = *_args.getPtr<Grid<Real> >("prod",3,&_lock); Grid<Real>& nuT = *_args.getPtr<Grid<Real> >("nuT",4,&_lock); Grid<Real>* strain = _args.getPtrOpt<Grid<Real> >("strain",5,0,&_lock); Real pscale = _args.getOpt<Real >("pscale",6,1.0f,&_lock); _retval = getPyNone(); KEpsilonComputeProduction(vel,k,eps,prod,nuT,strain,pscale); _args.check(); } pbFinalizePlugin(parent,"KEpsilonComputeProduction" ); return _retval; } catch(std::exception& e) { pbSetError("KEpsilonComputeProduction",e.what()); return 0; } } static const Pb::Register _RP_KEpsilonComputeProduction ("","KEpsilonComputeProduction",_W_0); + +//! Integrate source terms of k-epsilon equation + + struct KnAddTurbulenceSource : public KernelBase { KnAddTurbulenceSource(Grid<Real>& kgrid, Grid<Real>& egrid, const Grid<Real>& pgrid, Real dt) : KernelBase(&kgrid,0) ,kgrid(kgrid),egrid(egrid),pgrid(pgrid),dt(dt) { run(); } inline void op(int idx, Grid<Real>& kgrid, Grid<Real>& egrid, const Grid<Real>& pgrid, Real dt ) { + Real eps = egrid[idx], prod = pgrid[idx], ke = kgrid[idx]; + if (ke <= 0) ke = 1e-3; // pre-clamp to avoid nan + + Real newK = ke + dt*(prod - eps); + Real newEps = eps + dt*(prod * keC1 - eps * keC2) * (eps / ke); + if (newEps <= 0) newEps = 1e-4; // pre-clamp to avoid nan + + kgrid[idx] = newK; + egrid[idx] = newEps; +} inline Grid<Real>& getArg0() { return kgrid; } typedef Grid<Real> type0;inline Grid<Real>& getArg1() { return egrid; } typedef Grid<Real> type1;inline const Grid<Real>& getArg2() { return pgrid; } typedef Grid<Real> type2;inline Real& getArg3() { return dt; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, kgrid,egrid,pgrid,dt); } Grid<Real>& kgrid; Grid<Real>& egrid; const Grid<Real>& pgrid; Real dt; }; + + +//! Integrate source terms of k-epsilon equation +void KEpsilonSources(Grid<Real>& k, Grid<Real>& eps, Grid<Real>& prod) { + Real dt = k.getParent()->getDt(); + + KnAddTurbulenceSource(k, eps, prod, dt); + + // compute limits + const Real minK = 1.5*square(keU0)*square(keImin); + const Real maxK = 1.5*square(keU0)*square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "KEpsilonSources" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& k = *_args.getPtr<Grid<Real> >("k",0,&_lock); Grid<Real>& eps = *_args.getPtr<Grid<Real> >("eps",1,&_lock); Grid<Real>& prod = *_args.getPtr<Grid<Real> >("prod",2,&_lock); _retval = getPyNone(); KEpsilonSources(k,eps,prod); _args.check(); } pbFinalizePlugin(parent,"KEpsilonSources" ); return _retval; } catch(std::exception& e) { pbSetError("KEpsilonSources",e.what()); return 0; } } static const Pb::Register _RP_KEpsilonSources ("","KEpsilonSources",_W_1); + +//! Initialize the domain or boundary conditions +void KEpsilonBcs(FlagGrid& flags, Grid<Real>& k, Grid<Real>& eps, Real intensity, Real nu, bool fillArea) { + // compute limits + const Real vk = 1.5*square(keU0)*square(intensity); + const Real ve = keCmu*square(vk) / nu; + + FOR_IDX(k) { + if (fillArea || flags.isObstacle(idx)) { + k[idx] = vk; + eps[idx] = ve; + } + } +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "KEpsilonBcs" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& k = *_args.getPtr<Grid<Real> >("k",1,&_lock); Grid<Real>& eps = *_args.getPtr<Grid<Real> >("eps",2,&_lock); Real intensity = _args.get<Real >("intensity",3,&_lock); Real nu = _args.get<Real >("nu",4,&_lock); bool fillArea = _args.get<bool >("fillArea",5,&_lock); _retval = getPyNone(); KEpsilonBcs(flags,k,eps,intensity,nu,fillArea); _args.check(); } pbFinalizePlugin(parent,"KEpsilonBcs" ); return _retval; } catch(std::exception& e) { pbSetError("KEpsilonBcs",e.what()); return 0; } } static const Pb::Register _RP_KEpsilonBcs ("","KEpsilonBcs",_W_2); + +//! Gradient diffusion smoothing. Not unconditionally stable -- should probably do substepping etc. +void ApplyGradDiff(const Grid<Real>& grid, Grid<Real>& res, const Grid<Real>& nu, Real dt, Real sigma) { + // should do this (but requires better boundary handling) + /*MACGrid grad(grid.getParent()); + GradientOpMAC(grad, grid); + grad *= nu; + DivergenceOpMAC(res, grad); + res *= dt/sigma; */ + + LaplaceOp(res, grid); + res *= nu; + res *= dt/sigma; +} + +//! Compute k-epsilon turbulent viscosity +void KEpsilonGradientDiffusion(Grid<Real>& k, Grid<Real>& eps, Grid<Real>& nuT, Real sigmaU=4.0, MACGrid* vel=0) { + Real dt = k.getParent()->getDt(); + Grid<Real> res(k.getParent()); + + // gradient diffusion of k + ApplyGradDiff(k, res, nuT, dt, keS1); + k += res; + + // gradient diffusion of epsilon + ApplyGradDiff(eps, res, nuT, dt, keS2); + eps += res; + + // gradient diffusion of velocity + if (vel) { + Grid<Real> vc(k.getParent()); + for (int c=0; c<3; c++) { + GetComponent(*vel, vc, c); + ApplyGradDiff(vc, res, nuT, dt, sigmaU); + vc += res; + SetComponent(*vel, vc, c); + } + } +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "KEpsilonGradientDiffusion" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& k = *_args.getPtr<Grid<Real> >("k",0,&_lock); Grid<Real>& eps = *_args.getPtr<Grid<Real> >("eps",1,&_lock); Grid<Real>& nuT = *_args.getPtr<Grid<Real> >("nuT",2,&_lock); Real sigmaU = _args.getOpt<Real >("sigmaU",3,4.0,&_lock); MACGrid* vel = _args.getPtrOpt<MACGrid >("vel",4,0,&_lock); _retval = getPyNone(); KEpsilonGradientDiffusion(k,eps,nuT,sigmaU,vel); _args.check(); } pbFinalizePlugin(parent,"KEpsilonGradientDiffusion" ); return _retval; } catch(std::exception& e) { pbSetError("KEpsilonGradientDiffusion",e.what()); return 0; } } static const Pb::Register _RP_KEpsilonGradientDiffusion ("","KEpsilonGradientDiffusion",_W_3); + + + +} // namespace + diff --git a/source/blender/python/manta_pp/plugin/meshplugins.cpp b/source/blender/python/manta_pp/plugin/meshplugins.cpp new file mode 100644 index 00000000000..ec6e554553e --- /dev/null +++ b/source/blender/python/manta_pp/plugin/meshplugins.cpp @@ -0,0 +1,636 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Smoothing etc. for meshes + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#include <queue> +#include <algorithm> +#include "mesh.h" +#include "kernel.h" +#include "edgecollapse.h" +#include <mesh.h> +#include <stack> + +using namespace std; + +namespace Manta { + +//! Mesh smoothing +/*! see Desbrun 99 "Implicit fairing of of irregular meshes using diffusion and curvature flow"*/ +void smoothMesh(Mesh& mesh, Real strength, int steps = 1, Real minLength=1e-5) { + const Real dt = mesh.getParent()->getDt(); + const Real str = min(dt * strength, (Real)1); + mesh.rebuildQuickCheck(); + + // calculate original mesh volume + Vec3 origCM; + Real origVolume = mesh.computeCenterOfMass(origCM); + + // temp vertices + const int numCorners = mesh.numTris() * 3; + const int numNodes= mesh.numNodes(); + vector<Vec3> temp(numNodes); + vector<bool> visited(numNodes); + + for (int s = 0; s<steps; s++) { + // reset markers + for(size_t i=0; i<visited.size(); i++) visited[i] = false; + + for (int c = 0; c < numCorners; c++) { + const int node = mesh.corners(c).node; + if (visited[node]) continue; + + const Vec3 pos = mesh.nodes(node).pos; + Vec3 dx(_0); + Real totalLen = 0; + + // rotate around vertex + set<int>& ring = mesh.get1Ring(node).nodes; + for(set<int>::iterator it=ring.begin(); it!=ring.end(); it++) { + Vec3 edge = mesh.nodes(*it).pos - pos; + Real len = norm(edge); + + if (len > minLength) { + dx += edge * (_1/len); + totalLen += len; + } else { + totalLen = _0; + break; + } + } + visited[node] = true; + temp[node] = pos; + if (totalLen != 0) + temp[node] += dx * (str / totalLen); + } + + // copy back + for (int n=0; n<numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = temp[n]; + } + + // calculate new mesh volume + Vec3 newCM; + Real newVolume = mesh.computeCenterOfMass(newCM); + + // preserve volume : scale relative to CM + Real beta; +#if defined(WIN32) || defined(_WIN32) + beta = pow( (Real)abs(origVolume/newVolume), (Real)(1./3.) ); +#else + beta = cbrt( origVolume/newVolume ); +# endif + + for (int n=0; n<numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = origCM + (mesh.nodes(n).pos - newCM) * beta; +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "smoothMesh" ); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); Real strength = _args.get<Real >("strength",1,&_lock); int steps = _args.getOpt<int >("steps",2,1,&_lock); Real minLength = _args.getOpt<Real >("minLength",3,1e-5,&_lock); _retval = getPyNone(); smoothMesh(mesh,strength,steps,minLength); _args.check(); } pbFinalizePlugin(parent,"smoothMesh" ); return _retval; } catch(std::exception& e) { pbSetError("smoothMesh",e.what()); return 0; } } static const Pb::Register _RP_smoothMesh ("","smoothMesh",_W_0); + +//! Subdivide and edgecollapse to guarantee mesh with edgelengths between +//! min/maxLength and an angle below minAngle +void subdivideMesh(Mesh& mesh, Real minAngle, Real minLength, Real maxLength, bool cutTubes = false) { + // gather some statistics + int edgeSubdivs = 0, edgeCollsAngle = 0, edgeCollsLen = 0, edgeKill = 0; + mesh.rebuildQuickCheck(); + + vector<int> deletedNodes; + map<int,bool> taintedTris; + priority_queue<pair<Real,int> > pq; + + ////////////////////////////////////////// + // EDGE COLLAPSE // + // - particles marked for deletation // + ////////////////////////////////////////// + + for (int t=0; t<mesh.numTris(); t++) { + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // check if at least 2 nodes are marked for delete + bool k[3]; + int numKill = 0; + for (int i=0; i<3; i++) { + k[i] = mesh.nodes(mesh.tris(t).c[i]).flags & Mesh::NfKillme; + if (k[i]) numKill++; + } + if (numKill<2) continue; + + if (k[0] && k[1]) + CollapseEdge(mesh, t, 2, mesh.getEdge(t,0), mesh.getNode(t,0), deletedNodes, taintedTris, edgeKill, cutTubes); + else if (k[1] && k[2]) + CollapseEdge(mesh, t, 0, mesh.getEdge(t,1), mesh.getNode(t,1), deletedNodes, taintedTris, edgeKill, cutTubes); + else if (k[2] && k[0]) + CollapseEdge(mesh, t, 1, mesh.getEdge(t,2), mesh.getNode(t,2), deletedNodes, taintedTris, edgeKill, cutTubes); + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on small triangle angle // + ////////////////////////////////////////// + + if (minAngle > 0) { + for(int t=0; t<mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // if this triangle has already been deleted, ignore it + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // first we find the angles of this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Vec3 ne0 = e0; + Vec3 ne1 = e1; + Vec3 ne2 = e2; + normalize(ne0); + normalize(ne1); + normalize(ne2); + + //Real thisArea = sqrMag(cross(-e2,e0)); + // small angle approximation says sin(x) = arcsin(x) = x, + // arccos(x) = pi/2 - arcsin(x), + // cos(x) = dot(A,B), + // so angle is approximately 1 - dot(A,B). + Real angle[3]; + angle[0] = 1.0-dot(ne0,-ne2); + angle[1] = 1.0-dot(ne1,-ne0); + angle[2] = 1.0-dot(ne2,-ne1); + Real worstAngle = angle[0]; + int which = 0; + if(angle[1]<worstAngle) { + worstAngle = angle[1]; + which = 1; + } + if(angle[2]<worstAngle) { + worstAngle = angle[2]; + which = 2; + } + + // then we see if the angle is too small + if(worstAngle<minAngle) { + Vec3 edgevect; + Vec3 endpoint; + switch(which) { + case 0: + endpoint = mesh.getNode(t,1); + edgevect = e1; + break; + case 1: + endpoint = mesh.getNode(t,2); + edgevect = e2; + break; + case 2: + endpoint = mesh.getNode(t,0); + edgevect = e0; + break; + default: + break; + } + + CollapseEdge(mesh, t,which,edgevect,endpoint,deletedNodes,taintedTris, edgeCollsAngle, cutTubes); + } + } + } + + ////////////////////// + // EDGE SUBDIVISION // + ////////////////////// + + Real maxLength2 = maxLength*maxLength; + for (int t=0; t<mesh.numTris(); t++) { + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Real longest = max(d0,max(d1,d2)); + if(longest > maxLength2) { + pq.push(pair<Real,int>(longest,t)); + } + } + if (maxLength > 0) { + + while(!pq.empty() && pq.top().first>maxLength2) { + // we only want to run through the edge list ONCE + // and we want to subdivide the original edges before we subdivide any newer, shorter edges, + // so whenever we subdivide, we add the 2 new triangles on the end of the SurfaceTri vector + // and mark the original subdivided triangles for deletion. + // when we are done subdividing, we delete the obsolete triangles + + int triA = pq.top().second; + pq.pop(); + + if(taintedTris.find(triA)!=taintedTris.end()) + continue; + + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(triA,0), e1 = mesh.getEdge(triA,1), e2 = mesh.getEdge(triA,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + int which; + if(d0>d1) { + if(d0>d2) { + edgevect = e0; + endpoint = mesh.getNode(triA, 0);; + which = 2; // 2 opposite of edge 0-1 + } else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } else { + if(d1>d2) { + edgevect = e1; + endpoint = mesh.getNode(triA, 1); + which = 0; // 0 opposite of edge 1-2 + } else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + // This edge is too long, so we split it in the middle + + // * + // / \. + // /C0 \. + // / \. + // / \. + // / B \. + // / \. + // /C1 C2 \. + // *---------------* + // \C2 C1 / + // \ / + // \ A / + // \ / + // \ / + // \C0 / + // \ / + // * + // + // BECOMES + // + // * + // /|\. + // / | \. + // /C0|C0\. + // / | \. + // / B1 | B2 \. + // / | \. + // /C1 C2|C1 C2 \. + // *-------*-------* + // \C2 C1|C2 C1/ + // \ | / + // \ A2 | A1 / + // \ | / + // \C0|C0/ + // \ | / + // \|/ + // * + + int triB = -1; bool haveB = false; + Corner ca_old[3],cb_old[3]; + ca_old[0] = mesh.corners(triA, which); + ca_old[1] = mesh.corners(ca_old[0].next); + ca_old[2] = mesh.corners(ca_old[0].prev); + if (ca_old[0].opposite>=0) { + cb_old[0] = mesh.corners(ca_old[0].opposite); + cb_old[1] = mesh.corners(cb_old[0].next); + cb_old[2] = mesh.corners(cb_old[0].prev); + triB = cb_old[0].tri; + haveB = true; + } + //else throw Error("nonmanifold"); + + // subdivide in the middle of the edge and create new triangles + Node newNode; + newNode.flags = 0; + + newNode.pos = endpoint + 0.5*edgevect; // fallback: linear average + // default: use butterfly + if (haveB) + newNode.pos = ModifiedButterflySubdivision(mesh, ca_old[0], cb_old[0], newNode.pos); + + // find indices of two points of 'which'-edge + // merge flags + int P0 = ca_old[1].node; + int P1 = ca_old[2].node; + newNode.flags = mesh.nodes(P0).flags | mesh.nodes(P1).flags; + + Real len0 = norm(mesh.nodes(P0).pos - newNode.pos); + Real len1 = norm(mesh.nodes(P1).pos - newNode.pos); + + // remove P0/P1 1-ring connection + mesh.get1Ring(P0).nodes.erase(P1); + mesh.get1Ring(P1).nodes.erase(P0); + mesh.get1Ring(P0).tris.erase(triA); + mesh.get1Ring(P1).tris.erase(triA); + mesh.get1Ring(ca_old[0].node).tris.erase(triA); + if (haveB) { + mesh.get1Ring(P0).tris.erase(triB); + mesh.get1Ring(P1).tris.erase(triB); + mesh.get1Ring(cb_old[0].node).tris.erase(triB); + } + + // init channel properties for new node + for(int i=0; i<mesh.numNodeChannels(); i++) { + mesh.nodeChannel(i)->addInterpol(P0, P1, len0/(len0+len1)); + } + + // write to array + mesh.addTri(Triangle(ca_old[0].node, ca_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(ca_old[0].node, mesh.numNodes(), ca_old[2].node)); + if (haveB) { + mesh.addTri(Triangle(cb_old[0].node, cb_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(cb_old[0].node, mesh.numNodes(), cb_old[2].node)); + } + mesh.addNode(newNode); + + const int nt = haveB ? 4 : 2; + int triA1 = mesh.numTris()-nt; + int triA2 = mesh.numTris()-nt+1; + int triB1=0, triB2=0; + if (haveB) { + triB1 = mesh.numTris()-nt+2; + triB2 = mesh.numTris()-nt+3; + } + mesh.tris(triA1).flags = mesh.tris(triA).flags; + mesh.tris(triA2).flags = mesh.tris(triA).flags; + mesh.tris(triB1).flags = mesh.tris(triB).flags; + mesh.tris(triB2).flags = mesh.tris(triB).flags; + + // connect new triangles to outside triangles, + // and connect outside triangles to these new ones + for (int c=0; c<3; c++) mesh.addCorner(Corner(triA1,mesh.tris(triA1).c[c])); + for (int c=0; c<3; c++) mesh.addCorner(Corner(triA2,mesh.tris(triA2).c[c])); + if (haveB) { + for (int c=0; c<3; c++) mesh.addCorner(Corner(triB1,mesh.tris(triB1).c[c])); + for (int c=0; c<3; c++) mesh.addCorner(Corner(triB2,mesh.tris(triB2).c[c])); + } + + int baseIdx = 3*(mesh.numTris()-nt); + Corner* cBase = &mesh.corners(baseIdx); + + // set next/prev + for (int t=0; t<nt; t++) + for (int c=0; c<3; c++) { + cBase[t*3+c].next = baseIdx+t*3+((c+1)%3); + cBase[t*3+c].prev = baseIdx+t*3+((c+2)%3); + } + + // set opposites + // A1 + cBase[0].opposite = haveB ? (baseIdx+9) : -1; + cBase[1].opposite = baseIdx+5; + cBase[2].opposite = -1; + if (ca_old[2].opposite>=0) { + cBase[2].opposite = ca_old[2].opposite; + mesh.corners(cBase[2].opposite).opposite = baseIdx+2; + } + // A2 + cBase[3].opposite = haveB ? (baseIdx+6) : -1; + cBase[4].opposite = -1; + if (ca_old[1].opposite>=0) { + cBase[4].opposite = ca_old[1].opposite; + mesh.corners(cBase[4].opposite).opposite = baseIdx+4; + } + cBase[5].opposite = baseIdx+1; + if (haveB) { + // B1 + cBase[6].opposite = baseIdx+3; + cBase[7].opposite = baseIdx+11; + cBase[8].opposite = -1; + if (cb_old[2].opposite>=0) { + cBase[8].opposite = cb_old[2].opposite; + mesh.corners(cBase[8].opposite).opposite = baseIdx+8; + } + // B2 + cBase[9].opposite = baseIdx+0; + cBase[10].opposite = -1; + if (cb_old[1].opposite>=0) { + cBase[10].opposite = cb_old[1].opposite; + mesh.corners(cBase[10].opposite).opposite = baseIdx+10; + } + cBase[11].opposite = baseIdx+7; + } + + //////////////////// + // mark the two original triangles for deletion + taintedTris[triA] = true; + mesh.removeTriFromLookup(triA); + if (haveB) { + taintedTris[triB] = true; + mesh.removeTriFromLookup(triB); + } + + Real areaA1 = mesh.getFaceArea(triA1), areaA2 = mesh.getFaceArea(triA2); + Real areaB1=0, areaB2=0; + if (haveB) { + areaB1 = mesh.getFaceArea(triB1); + areaB2 = mesh.getFaceArea(triB2); + } + + // add channel props for new triangles + for(int i=0; i<mesh.numTriChannels(); i++) { + mesh.triChannel(i)->addSplit(triA, areaA1/(areaA1+areaA2)); + mesh.triChannel(i)->addSplit(triA, areaA2/(areaA1+areaA2)); + if (haveB) { + mesh.triChannel(i)->addSplit(triB, areaB1/(areaB1+areaB2)); + mesh.triChannel(i)->addSplit(triB, areaB2/(areaB1+areaB2)); + } + } + + // add the four new triangles to the prority queue + for(int i=mesh.numTris()-nt; i<mesh.numTris(); i++) { + // find the maximum length edge in this triangle + Vec3 ne0 = mesh.getEdge(i, 0), ne1 = mesh.getEdge(i, 1), ne2 = mesh.getEdge(i, 2); + Real nd0 = normSquare(ne0); + Real nd1 = normSquare(ne1); + Real nd2 = normSquare(ne2); + Real longest = max(nd0,max(nd1,nd2)); + //longest = (int)(longest * 1e2) / 1e2; // HACK: truncate + pq.push(pair<Real,int>(longest,i)); + } + edgeSubdivs++; + } + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on short edge length // + ////////////////////////////////////////// + if (minLength > 0) { + const Real minLength2 = minLength*minLength; + for(int t=0; t<mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // NOTE: + // priority queue does not work so great in the edge collapse case, + // because collapsing one triangle affects the edge lengths + // of many neighbor triangles, + // and we do not update their maximum edge length in the queue. + + // if this triangle has already been deleted, ignore it + //if(taintedTris[t]) + // continue; + + if(taintedTris.find(t)!=taintedTris.end()) + continue; + + // first we find the minimum length edge in this triangle + Vec3 e0 = mesh.getEdge(t,0), e1 = mesh.getEdge(t,1), e2 = mesh.getEdge(t,2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + Real dist2; + int which; + if(d0<d1) { + if(d0<d2) { + dist2 = d0; + edgevect = e0; + endpoint = mesh.getNode(t,0); + which = 2; // 2 opposite of edge 0-1 + } else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t,2); + which = 1; // 1 opposite of edge 2-0 + } + } else { + if(d1<d2) { + dist2 = d1; + edgevect = e1; + endpoint = mesh.getNode(t,1); + which = 0; // 0 opposite of edge 1-2 + } else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t,2); + which = 1; // 1 opposite of edge 2-0 + } + } + // then we see if the min length edge is too short + if(dist2<minLength2) { + CollapseEdge(mesh, t,which,edgevect,endpoint, deletedNodes,taintedTris, edgeCollsLen, cutTubes); + } + } + } + // cleanup nodes and triangles marked for deletion + + // we run backwards through the deleted array, + // replacing triangles with ones from the back + // (this avoids the potential problem of overwriting a triangle + // with a to-be-deleted triangle) + std::map<int,bool>::reverse_iterator tti = taintedTris.rbegin(); + for(;tti!=taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + cout << "Surface subdivision finished with " << mesh.numNodes() << " surface nodes and " << mesh.numTris(); + cout << " surface triangles, edgeSubdivs:" << edgeSubdivs << ", edgeCollapses: " << edgeCollsLen; + cout << " + " << edgeCollsAngle << " + " << edgeKill << endl; + //mesh.sanityCheck(); + +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "subdivideMesh" ); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); Real minAngle = _args.get<Real >("minAngle",1,&_lock); Real minLength = _args.get<Real >("minLength",2,&_lock); Real maxLength = _args.get<Real >("maxLength",3,&_lock); bool cutTubes = _args.getOpt<bool >("cutTubes",4,false,&_lock); _retval = getPyNone(); subdivideMesh(mesh,minAngle,minLength,maxLength,cutTubes); _args.check(); } pbFinalizePlugin(parent,"subdivideMesh" ); return _retval; } catch(std::exception& e) { pbSetError("subdivideMesh",e.what()); return 0; } } static const Pb::Register _RP_subdivideMesh ("","subdivideMesh",_W_1); + +void killSmallComponents(Mesh& mesh, int elements = 10) { + const int num = mesh.numTris(); + vector<int> comp(num); + vector<int> numEl; + vector<int> deletedNodes; + vector<bool> isNodeDel(mesh.numNodes()); + map<int,bool> taintedTris; + // enumerate components + int cur=0; + for (int i=0; i<num; i++) { + if (comp[i]==0) { + cur++; + comp[i] = cur; + + stack<int> stack; + stack.push(i); + int cnt = 1; + while(!stack.empty()) { + int tri = stack.top(); + stack.pop(); + for (int c=0; c<3; c++) { + int op = mesh.corners(tri,c).opposite; + if (op < 0) continue; + int ntri = mesh.corners(op).tri; + if (comp[ntri]==0) { + comp[ntri] = cur; + stack.push(ntri); + cnt++; + } + } + } + numEl.push_back(cnt); + } + } + // kill small components + for (int j=0; j<num; j++) { + if (numEl[comp[j]-1] < elements) { + taintedTris[j] = true; + for (int c=0; c<3; c++) { + int n=mesh.tris(j).c[c]; + if (!isNodeDel[n]) { + isNodeDel[n] = true; + deletedNodes.push_back(n); + } + } + } + } + + std::map<int,bool>::reverse_iterator tti = taintedTris.rbegin(); + for(;tti!=taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + + if (!taintedTris.empty()) + cout << "Killed small components : " << deletedNodes.size() << " nodes, " << taintedTris.size() << " tris deleted." << endl; +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "killSmallComponents" ); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); int elements = _args.getOpt<int >("elements",1,10,&_lock); _retval = getPyNone(); killSmallComponents(mesh,elements); _args.check(); } pbFinalizePlugin(parent,"killSmallComponents" ); return _retval; } catch(std::exception& e) { pbSetError("killSmallComponents",e.what()); return 0; } } static const Pb::Register _RP_killSmallComponents ("","killSmallComponents",_W_2); + + +} //namespace + + + diff --git a/source/blender/python/manta_pp/plugin/pressure.cpp b/source/blender/python/manta_pp/plugin/pressure.cpp new file mode 100644 index 00000000000..3fa6f2da1c8 --- /dev/null +++ b/source/blender/python/manta_pp/plugin/pressure.cpp @@ -0,0 +1,327 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugins for pressure correction: solve_pressure, and ghost fluid helpers + * + ******************************************************************************/ +#include "vectorbase.h" +#include "kernel.h" +#include "conjugategrad.h" + +using namespace std; +namespace Manta { + +//! Kernel: Construct the right-hand side of the poisson equation + + + + struct MakeRhs : public KernelBase { MakeRhs(FlagGrid& flags, Grid<Real>& rhs, MACGrid& vel, Grid<Real>* perCellCorr) : KernelBase(&flags,1) ,flags(flags),rhs(rhs),vel(vel),perCellCorr(perCellCorr) ,cnt(0),sum(0) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& rhs, MACGrid& vel, Grid<Real>* perCellCorr ,int& cnt,double& sum) { + if (!flags.isFluid(i,j,k)) { + rhs(i,j,k) = 0; + return; + } + + // compute divergence + // no flag checks: assumes vel at obstacle interfaces is set to zero + Real set = vel(i,j,k).x - vel(i+1,j,k).x + + vel(i,j,k).y - vel(i,j+1,k).y; + if(vel.is3D()) set+=vel(i,j,k).z - vel(i,j,k+1).z; + + // per cell divergence correction + if(perCellCorr) + set += perCellCorr->get(i,j,k); + + // obtain sum, cell count + sum += set; + cnt++; + + rhs(i,j,k) = set; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return rhs; } typedef Grid<Real> type1;inline MACGrid& getArg2() { return vel; } typedef MACGrid type2;inline Grid<Real>* getArg3() { return perCellCorr; } typedef Grid<Real> type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,rhs,vel,perCellCorr,cnt,sum); } FlagGrid& flags; Grid<Real>& rhs; MACGrid& vel; Grid<Real>* perCellCorr; int cnt; double sum; }; + +//! Kernel: Apply velocity update from poisson equation + + + struct CorrectVelocity : public KernelBase { CorrectVelocity(FlagGrid& flags, MACGrid& vel, Grid<Real>& pressure) : KernelBase(&flags,1) ,flags(flags),vel(vel),pressure(pressure) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Grid<Real>& pressure ) { + int idx = flags.index(i,j,k); + if (flags.isFluid(idx)) + { + if (flags.isFluid(i-1,j,k)) vel[idx].x -= (pressure[idx] - pressure(i-1,j,k)); + if (flags.isFluid(i,j-1,k)) vel[idx].y -= (pressure[idx] - pressure(i,j-1,k)); + if (flags.is3D() && flags.isFluid(i,j,k-1)) vel[idx].z -= (pressure[idx] - pressure(i,j,k-1)); + + if (flags.isEmpty(i-1,j,k)) vel[idx].x -= pressure[idx]; + if (flags.isEmpty(i,j-1,k)) vel[idx].y -= pressure[idx]; + if (flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx].z -= pressure[idx]; + } + else if (flags.isEmpty(idx)) + { + if (flags.isFluid(i-1,j,k)) vel[idx].x += pressure(i-1,j,k); + else vel[idx].x = 0.f; + if (flags.isFluid(i,j-1,k)) vel[idx].y += pressure(i,j-1,k); + else vel[idx].y = 0.f; + if (flags.is3D() ) { + if (flags.isFluid(i,j,k-1)) vel[idx].z += pressure(i,j,k-1); + else vel[idx].z = 0.f; + } + } +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Grid<Real>& getArg2() { return pressure; } typedef Grid<Real> type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,vel,pressure); } FlagGrid& flags; MACGrid& vel; Grid<Real>& pressure; }; + + + + + + struct SetOpenBound : public KernelBase { SetOpenBound(Grid<Real> &A0,Grid<Real> &Ai,Grid<Real> &Aj,Grid<Real> &Ak,FlagGrid& flags,MACGrid& vel, Vector3D<bool> lo, Vector3D<bool> up) : KernelBase(&A0,0) ,A0(A0),Ai(Ai),Aj(Aj),Ak(Ak),flags(flags),vel(vel),lo(lo),up(up) { run(); } inline void op(int i, int j, int k, Grid<Real> &A0,Grid<Real> &Ai,Grid<Real> &Aj,Grid<Real> &Ak,FlagGrid& flags,MACGrid& vel, Vector3D<bool> lo, Vector3D<bool> up ) { + + if (!flags.isFluid(i,j,k)) + return; + + int b = flags.getBoundaryWidth(); + + // set matrix stencil in and at boundary to empty + if((lo.x && i <= b+1)||(up.x && i >= maxX-b-2)||(lo.y && j <= b+1)||(up.y && j >= maxY-b-2)) + A0(i,j,k) = (flags.is3D()) ? 6. : 4.; + + if ((lo.x && i <= b)||(up.x && i >= maxX-b-2)) Ai(i,j,k) = .0; + if ((lo.y && j <= b)||(up.y && j >= maxY-b-2)) Aj(i,j,k) = .0; + if (flags.is3D() && ((lo.z && k <= b)||(up.z && k >= maxZ-b-2))) Ak(i,j,k) = .0; + + // set velocity boundary conditions + if (lo.x && i == b) vel(b,j,k) = vel(b+1,j,k); + if (lo.y && j == b) vel(i,b,k) = vel(i,b+1,k); + if (up.x && i == maxX-b-1) vel(maxX-b-1,j,k) = vel(maxX-b-2,j,k); + if (up.y && j == maxY-b-1) vel(i,maxY-b-1,k) = vel(i,maxY-b-2,k); + if(flags.is3D()) { + if (lo.z && k == b) vel(i,j,b) = vel(i,j,b+1); + if (up.z && k == maxZ-b-1) vel(i,j,maxZ-b-1) = vel(i,j,maxZ-b-2); + } +} inline Grid<Real> & getArg0() { return A0; } typedef Grid<Real> type0;inline Grid<Real> & getArg1() { return Ai; } typedef Grid<Real> type1;inline Grid<Real> & getArg2() { return Aj; } typedef Grid<Real> type2;inline Grid<Real> & getArg3() { return Ak; } typedef Grid<Real> type3;inline FlagGrid& getArg4() { return flags; } typedef FlagGrid type4;inline MACGrid& getArg5() { return vel; } typedef MACGrid type5;inline Vector3D<bool> & getArg6() { return lo; } typedef Vector3D<bool> type6;inline Vector3D<bool> & getArg7() { return up; } typedef Vector3D<bool> type7; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, A0,Ai,Aj,Ak,flags,vel,lo,up); } Grid<Real> & A0; Grid<Real> & Ai; Grid<Real> & Aj; Grid<Real> & Ak; FlagGrid& flags; MACGrid& vel; Vector3D<bool> lo; Vector3D<bool> up; }; + + +//! Kernel: Set matrix rhs for outflow + + struct SetOutflow : public KernelBase { SetOutflow(Grid<Real>& rhs, Vector3D<bool> lowerBound, Vector3D<bool> upperBound, int height) : KernelBase(&rhs,0) ,rhs(rhs),lowerBound(lowerBound),upperBound(upperBound),height(height) { run(); } inline void op(int i, int j, int k, Grid<Real>& rhs, Vector3D<bool> lowerBound, Vector3D<bool> upperBound, int height ) { + if ((lowerBound.x && i < height) || (upperBound.x && i >= maxX-1-height) || + (lowerBound.y && j < height) || (upperBound.y && j >= maxY-1-height) || + (lowerBound.z && k < height) || (upperBound.z && k >= maxZ-1-height)) + rhs(i,j,k) = 0; +} inline Grid<Real>& getArg0() { return rhs; } typedef Grid<Real> type0;inline Vector3D<bool> & getArg1() { return lowerBound; } typedef Vector3D<bool> type1;inline Vector3D<bool> & getArg2() { return upperBound; } typedef Vector3D<bool> type2;inline int& getArg3() { return height; } typedef int type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, rhs,lowerBound,upperBound,height); } Grid<Real>& rhs; Vector3D<bool> lowerBound; Vector3D<bool> upperBound; int height; }; + + +// ***************************************************************************** +// Ghost fluid helpers + +// calculate fraction filled with liquid (note, assumes inside value is < outside!) +inline static Real thetaHelper(Real inside, Real outside) +{ + Real denom = inside-outside; + if (denom > -1e-04) return 0.5; // should always be neg, and large enough... + return std::max(Real(0), std::min(Real(1), inside/denom)); +} + +// calculate ghost fluid factor, cell at idx should be a fluid cell +inline static Real ghostFluidHelper(int idx, int offset, const Grid<Real> &phi, Real gfClamp) +{ + Real alpha = thetaHelper(phi[idx], phi[idx+offset]); + if (alpha < gfClamp) return alpha = gfClamp; + return (1-(1/alpha)); +} + +//! Kernel: Adapt A0 for ghost fluid + + + struct ApplyGhostFluidDiagonal : public KernelBase { ApplyGhostFluidDiagonal(Grid<Real> &A0, const FlagGrid &flags, const Grid<Real> &phi, Real gfClamp) : KernelBase(&A0,1) ,A0(A0),flags(flags),phi(phi),gfClamp(gfClamp) { run(); } inline void op(int i, int j, int k, Grid<Real> &A0, const FlagGrid &flags, const Grid<Real> &phi, Real gfClamp ) { + const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + int idx = flags.index(i,j,k); + if (!flags.isFluid(idx)) return; + + if (flags.isEmpty(i-1,j,k)) A0[idx] -= ghostFluidHelper(idx, -X, phi, gfClamp); + if (flags.isEmpty(i+1,j,k)) A0[idx] -= ghostFluidHelper(idx, +X, phi, gfClamp); + if (flags.isEmpty(i,j-1,k)) A0[idx] -= ghostFluidHelper(idx, -Y, phi, gfClamp); + if (flags.isEmpty(i,j+1,k)) A0[idx] -= ghostFluidHelper(idx, +Y, phi, gfClamp); + if (flags.is3D()) { + if (flags.isEmpty(i,j,k-1)) A0[idx] -= ghostFluidHelper(idx, -Z, phi, gfClamp); + if (flags.isEmpty(i,j,k+1)) A0[idx] -= ghostFluidHelper(idx, +Z, phi, gfClamp); + } +} inline Grid<Real> & getArg0() { return A0; } typedef Grid<Real> type0;inline const FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline const Grid<Real> & getArg2() { return phi; } typedef Grid<Real> type2;inline Real& getArg3() { return gfClamp; } typedef Real type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, A0,flags,phi,gfClamp); } Grid<Real> & A0; const FlagGrid& flags; const Grid<Real> & phi; Real gfClamp; }; + +//! Kernel: Apply velocity update: ghost fluid contribution + + + struct CorrectVelocityGhostFluid : public KernelBase { CorrectVelocityGhostFluid(MACGrid &vel, const FlagGrid &flags, const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp) : KernelBase(&vel,1) ,vel(vel),flags(flags),pressure(pressure),phi(phi),gfClamp(gfClamp) { run(); } inline void op(int i, int j, int k, MACGrid &vel, const FlagGrid &flags, const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp ) { + const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + const int idx = flags.index(i,j,k); + if (flags.isFluid(idx)) + { + if (flags.isEmpty(i-1,j,k)) vel[idx][0] += pressure[idx] * ghostFluidHelper(idx, -X, phi, gfClamp); + if (flags.isEmpty(i,j-1,k)) vel[idx][1] += pressure[idx] * ghostFluidHelper(idx, -Y, phi, gfClamp); + if (flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx][2] += pressure[idx] * ghostFluidHelper(idx, -Z, phi, gfClamp); + } + else if (flags.isEmpty(idx)) + { + if (flags.isFluid(i-1,j,k)) vel[idx][0] -= pressure(i-1,j,k) * ghostFluidHelper(idx-X, +X, phi, gfClamp); + else vel[idx].x = 0.f; + if (flags.isFluid(i,j-1,k)) vel[idx][1] -= pressure(i,j-1,k) * ghostFluidHelper(idx-Y, +Y, phi, gfClamp); + else vel[idx].y = 0.f; + if (flags.is3D() ) { + if (flags.isFluid(i,j,k-1)) vel[idx][2] -= pressure(i,j,k-1) * ghostFluidHelper(idx-Z, +Z, phi, gfClamp); + else vel[idx].z = 0.f; + } + } +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline const FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline const Grid<Real> & getArg2() { return pressure; } typedef Grid<Real> type2;inline const Grid<Real> & getArg3() { return phi; } typedef Grid<Real> type3;inline Real& getArg4() { return gfClamp; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,flags,pressure,phi,gfClamp); } MACGrid& vel; const FlagGrid& flags; const Grid<Real> & pressure; const Grid<Real> & phi; Real gfClamp; }; + + +// improve behavior of clamping for large time steps: + +inline static Real ghostFluidWasClamped(int idx, int offset, const Grid<Real> &phi, Real gfClamp) +{ + Real alpha = thetaHelper(phi[idx], phi[idx+offset]); + if (alpha < gfClamp) return true; + return false; +} + + + + + struct ReplaceClampedGhostFluidVels : public KernelBase { ReplaceClampedGhostFluidVels(MACGrid &vel, FlagGrid &flags, const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp ) : KernelBase(&vel,1) ,vel(vel),flags(flags),pressure(pressure),phi(phi),gfClamp(gfClamp) { run(); } inline void op(int i, int j, int k, MACGrid &vel, FlagGrid &flags, const Grid<Real> &pressure, const Grid<Real> &phi, Real gfClamp ) { + const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + const int idx = flags.index(i,j,k); + if (flags.isFluid(idx)) + { + if( (flags.isEmpty(i-1,j,k)) && (ghostFluidWasClamped(idx, -X, phi, gfClamp)) ) + vel[idx-X][0] = vel[idx][0]; + if( (flags.isEmpty(i,j-1,k)) && (ghostFluidWasClamped(idx, -Y, phi, gfClamp)) ) + vel[idx-Y][1] = vel[idx][1]; + if( flags.is3D() && + (flags.isEmpty(i,j,k-1)) && (ghostFluidWasClamped(idx, -Z, phi, gfClamp)) ) + vel[idx-Z][2] = vel[idx][2]; + } + else if (flags.isEmpty(idx)) + { + if( (i>-1) && (flags.isFluid(i-1,j,k)) && ( ghostFluidWasClamped(idx-X, +X, phi, gfClamp) ) ) + vel[idx][0] = vel[idx-X][0]; + if( (j>-1) && (flags.isFluid(i,j-1,k)) && ( ghostFluidWasClamped(idx-Y, +Y, phi, gfClamp) ) ) + vel[idx][1] = vel[idx-Y][1]; + if( flags.is3D() && + ( (k>-1) && (flags.isFluid(i,j,k-1)) && ( ghostFluidWasClamped(idx-Z, +Z, phi, gfClamp) ) )) + vel[idx][2] = vel[idx-Z][2]; + } +} inline MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline const Grid<Real> & getArg2() { return pressure; } typedef Grid<Real> type2;inline const Grid<Real> & getArg3() { return phi; } typedef Grid<Real> type3;inline Real& getArg4() { return gfClamp; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,flags,pressure,phi,gfClamp); } MACGrid& vel; FlagGrid& flags; const Grid<Real> & pressure; const Grid<Real> & phi; Real gfClamp; }; + + +// ***************************************************************************** +// Main pressure solve + +inline void convertDescToVec(const string& desc, Vector3D<bool>& lo, Vector3D<bool>& up) { + for(size_t i=0; i<desc.size(); i++) { + if (desc[i] == 'x') lo.x = true; + else if (desc[i] == 'y') lo.y = true; + else if (desc[i] == 'z') lo.z = true; + else if (desc[i] == 'X') up.x = true; + else if (desc[i] == 'Y') up.y = true; + else if (desc[i] == 'Z') up.z = true; + else errMsg("invalid character in boundary description string. Only [xyzXYZ] allowed."); + } +} + +//! Perform pressure projection of the velocity grid + + + + + + + + + + + +void solvePressure(MACGrid& vel, Grid<Real>& pressure, FlagGrid& flags, string openBound="", Grid<Real>* phi = 0, Grid<Real>* perCellCorr = 0, Real gfClamp = 1e-04, Real cgMaxIterFac = 1.5, Real cgAccuracy = 1e-3, string outflow = "", int outflowHeight = 1, bool precondition = true, bool enforceCompatibility = false, bool useResNorm = true ) { + // parse strings + Vector3D<bool> loOpenBound, upOpenBound, loOutflow, upOutflow; + convertDescToVec(openBound, loOpenBound, upOpenBound); + convertDescToVec(outflow, loOutflow, upOutflow); + if (vel.is2D() && (loOpenBound.z || upOpenBound.z)) + errMsg("open boundaries for z specified for 2D grid"); + + // reserve temp grids + FluidSolver* parent = flags.getParent(); + Grid<Real> rhs(parent); + Grid<Real> residual(parent); + Grid<Real> search(parent); + Grid<Real> A0(parent); + Grid<Real> Ai(parent); + Grid<Real> Aj(parent); + Grid<Real> Ak(parent); + Grid<Real> tmp(parent); + Grid<Real> pca0(parent); + Grid<Real> pca1(parent); + Grid<Real> pca2(parent); + Grid<Real> pca3(parent); + + // setup matrix and boundaries + MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak); + SetOpenBound (A0, Ai, Aj, Ak, flags, vel, loOpenBound, upOpenBound); + + if (phi) { + ApplyGhostFluidDiagonal(A0, flags, *phi, gfClamp); + } + + // compute divergence and init right hand side + MakeRhs kernMakeRhs (flags, rhs, vel, perCellCorr); + + if (!outflow.empty()) + SetOutflow (rhs, loOutflow, upOutflow, outflowHeight); + + if (enforceCompatibility) + rhs += (Real)(-kernMakeRhs.sum / (Real)kernMakeRhs.cnt); + + // CG setup + // note: the last factor increases the max iterations for 2d, which right now can't use a preconditioner + const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); + GridCgInterface *gcg; + if (vel.is3D()) + gcg = new GridCg<ApplyMatrix> (pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); + else + gcg = new GridCg<ApplyMatrix2D>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); + + gcg->setAccuracy( cgAccuracy ); + gcg->setUseResNorm( useResNorm ); + + // optional preconditioning + gcg->setPreconditioner( precondition ? GridCgInterface::PC_mICP : GridCgInterface::PC_None, &pca0, &pca1, &pca2, &pca3); + + for (int iter=0; iter<maxIter; iter++) { + if (!gcg->iterate()) iter=maxIter; + } + debMsg("FluidSolver::solvePressure iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1); + delete gcg; + + CorrectVelocity(flags, vel, pressure ); + if (phi) { + CorrectVelocityGhostFluid (vel, flags, pressure, *phi, gfClamp); + // improve behavior of clamping for large time steps: + ReplaceClampedGhostFluidVels (vel, flags, pressure, *phi, gfClamp); + } +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "solvePressure" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Real>& pressure = *_args.getPtr<Grid<Real> >("pressure",1,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",2,&_lock); string openBound = _args.getOpt<string >("openBound",3,"",&_lock); Grid<Real>* phi = _args.getPtrOpt<Grid<Real> >("phi",4,0,&_lock); Grid<Real>* perCellCorr = _args.getPtrOpt<Grid<Real> >("perCellCorr",5,0,&_lock); Real gfClamp = _args.getOpt<Real >("gfClamp",6,1e-04,&_lock); Real cgMaxIterFac = _args.getOpt<Real >("cgMaxIterFac",7,1.5,&_lock); Real cgAccuracy = _args.getOpt<Real >("cgAccuracy",8,1e-3,&_lock); string outflow = _args.getOpt<string >("outflow",9,"",&_lock); int outflowHeight = _args.getOpt<int >("outflowHeight",10,1,&_lock); bool precondition = _args.getOpt<bool >("precondition",11,true,&_lock); bool enforceCompatibility = _args.getOpt<bool >("enforceCompatibility",12,false,&_lock); bool useResNorm = _args.getOpt<bool >("useResNorm",13,true ,&_lock); _retval = getPyNone(); solvePressure(vel,pressure,flags,openBound,phi,perCellCorr,gfClamp,cgMaxIterFac,cgAccuracy,outflow,outflowHeight,precondition,enforceCompatibility,useResNorm); _args.check(); } pbFinalizePlugin(parent,"solvePressure" ); return _retval; } catch(std::exception& e) { pbSetError("solvePressure",e.what()); return 0; } } static const Pb::Register _RP_solvePressure ("","solvePressure",_W_0); + +} // end namespace + + + diff --git a/source/blender/python/manta_pp/plugin/vortexplugins.cpp b/source/blender/python/manta_pp/plugin/vortexplugins.cpp new file mode 100644 index 00000000000..c0133697bee --- /dev/null +++ b/source/blender/python/manta_pp/plugin/vortexplugins.cpp @@ -0,0 +1,324 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugins for using vortex sheet meshes + * + ******************************************************************************/ + +#include <iostream> +#include "vortexsheet.h" +#include "vortexpart.h" +#include "shapes.h" +#include "commonkernels.h" +#include "conjugategrad.h" +#include "randomstream.h" +#include "levelset.h" + +using namespace std; + +namespace Manta { + +//! Mark area of mesh inside shape as fixed nodes. +//! Remove all other fixed nodes if 'exclusive' is set + +void markAsFixed(Mesh& mesh, Shape* shape, bool exclusive=true) { + for (int i=0; i<mesh.numNodes(); i++) { + if (shape->isInside(mesh.nodes(i).pos)) + mesh.nodes(i).flags |= Mesh::NfFixed; + else if (exclusive) + mesh.nodes(i).flags &= ~Mesh::NfFixed; + } +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "markAsFixed" ); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); Shape* shape = _args.getPtr<Shape >("shape",1,&_lock); bool exclusive = _args.getOpt<bool >("exclusive",2,true,&_lock); _retval = getPyNone(); markAsFixed(mesh,shape,exclusive); _args.check(); } pbFinalizePlugin(parent,"markAsFixed" ); return _retval; } catch(std::exception& e) { pbSetError("markAsFixed",e.what()); return 0; } } static const Pb::Register _RP_markAsFixed ("","markAsFixed",_W_0); + +//! Adapt texture coordinates of mesh inside shape +//! to obtain an effective inflow effect + +void texcoordInflow(VortexSheetMesh& mesh, Shape* shape, MACGrid& vel) { + static Vec3 t0 = Vec3::Zero; + + // get mean velocity + int cnt=0; + Vec3 meanV(_0); + FOR_IJK(vel) { + if (shape->isInsideGrid(i,j,k)) { + cnt++; + meanV += vel.getCentered(i,j,k); + } + } + meanV /= (Real) cnt; + t0 -= mesh.getParent()->getDt() * meanV; + mesh.setReferenceTexOffset(t0); + + // apply mean velocity + for (int i=0; i<mesh.numNodes(); i++) { + if (shape->isInside(mesh.nodes(i).pos)) { + Vec3 tc = mesh.nodes(i).pos + t0; + mesh.tex1(i) = tc; + mesh.tex2(i) = tc; + } + } +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "texcoordInflow" ); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh& mesh = *_args.getPtr<VortexSheetMesh >("mesh",0,&_lock); Shape* shape = _args.getPtr<Shape >("shape",1,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",2,&_lock); _retval = getPyNone(); texcoordInflow(mesh,shape,vel); _args.check(); } pbFinalizePlugin(parent,"texcoordInflow" ); return _retval; } catch(std::exception& e) { pbSetError("texcoordInflow",e.what()); return 0; } } static const Pb::Register _RP_texcoordInflow ("","texcoordInflow",_W_1); ; + +//! Init smoke density values of the mesh surface inside source shape + +void meshSmokeInflow(VortexSheetMesh& mesh, Shape* shape, Real amount) { + for (int t=0; t<mesh.numTris(); t++) { + if (shape->isInside(mesh.getFaceCenter(t))) + mesh.sheet(t).smokeAmount = amount; + } +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "meshSmokeInflow" ); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh& mesh = *_args.getPtr<VortexSheetMesh >("mesh",0,&_lock); Shape* shape = _args.getPtr<Shape >("shape",1,&_lock); Real amount = _args.get<Real >("amount",2,&_lock); _retval = getPyNone(); meshSmokeInflow(mesh,shape,amount); _args.check(); } pbFinalizePlugin(parent,"meshSmokeInflow" ); return _retval; } catch(std::exception& e) { pbSetError("meshSmokeInflow",e.what()); return 0; } } static const Pb::Register _RP_meshSmokeInflow ("","meshSmokeInflow",_W_2); + + + struct KnAcceleration : public KernelBase { KnAcceleration(MACGrid& a, const MACGrid& v1, const MACGrid& v0, const Real idt) : KernelBase(&a,0) ,a(a),v1(v1),v0(v0),idt(idt) { run(); } inline void op(int idx, MACGrid& a, const MACGrid& v1, const MACGrid& v0, const Real idt ) { + a[idx] = (v1[idx]-v0[idx])*idt; +} inline MACGrid& getArg0() { return a; } typedef MACGrid type0;inline const MACGrid& getArg1() { return v1; } typedef MACGrid type1;inline const MACGrid& getArg2() { return v0; } typedef MACGrid type2;inline const Real& getArg3() { return idt; } typedef Real type3; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, a,v1,v0,idt); } MACGrid& a; const MACGrid& v1; const MACGrid& v0; const Real idt; }; + +//! Add vorticity to vortex sheets based on buoyancy + + + +void vorticitySource(VortexSheetMesh& mesh, Vec3 gravity, MACGrid* vel=NULL, MACGrid* velOld=NULL, Real scale = 0.1, Real maxAmount = 0, Real mult = 1.0) { + Real dt = mesh.getParent()->getDt(); + Real dx = mesh.getParent()->getDx(); + MACGrid acceleration(mesh.getParent()); + if (vel) + KnAcceleration(acceleration, *vel, *velOld, 1.0/dt); + const Real A= -1.0; + Real maxV = 0, meanV = 0; + + for (int t=0; t<mesh.numTris(); t++) { + Vec3 fn = mesh.getFaceNormal(t); + Vec3 source; + if (vel) { + Vec3 a = acceleration.getInterpolated(mesh.getFaceCenter(t)); + source = A*cross(fn, a-gravity) * scale; + } else { + source = A*cross(fn, -gravity) * scale; + } + + if (mesh.isTriangleFixed(t)) source = 0; + + mesh.sheet(t).vorticity *= mult; + mesh.sheet(t).vorticity += dt * source / dx; + // upper limit + Real v = norm(mesh.sheet(t).vorticity); + if (maxAmount>0 && v > maxAmount) + mesh.sheet(t).vorticity *= maxAmount/v; + + //stats + if (v > maxV) maxV = v; + meanV += v; + } + + cout << "vorticity: max " << maxV << " / mean " << meanV/mesh.numTris() << endl; +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "vorticitySource" ); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh& mesh = *_args.getPtr<VortexSheetMesh >("mesh",0,&_lock); Vec3 gravity = _args.get<Vec3 >("gravity",1,&_lock); MACGrid* vel = _args.getPtrOpt<MACGrid >("vel",2,NULL,&_lock); MACGrid* velOld = _args.getPtrOpt<MACGrid >("velOld",3,NULL,&_lock); Real scale = _args.getOpt<Real >("scale",4,0.1,&_lock); Real maxAmount = _args.getOpt<Real >("maxAmount",5,0,&_lock); Real mult = _args.getOpt<Real >("mult",6,1.0,&_lock); _retval = getPyNone(); vorticitySource(mesh,gravity,vel,velOld,scale,maxAmount,mult); _args.check(); } pbFinalizePlugin(parent,"vorticitySource" ); return _retval; } catch(std::exception& e) { pbSetError("vorticitySource",e.what()); return 0; } } static const Pb::Register _RP_vorticitySource ("","vorticitySource",_W_3); + + +void smoothVorticity(VortexSheetMesh& mesh, int iter=1, Real sigma=0.2, Real alpha=0.8) { + const Real mult = -0.5 / sigma / sigma; + + // pre-calculate positions and weights + vector<Vec3> vort(mesh.numTris()), pos(mesh.numTris()); + vector<Real> weights(3*mesh.numTris()); + vector<int> index(3*mesh.numTris()); + for(int i=0; i<mesh.numTris(); i++) { + pos[i] = mesh.getFaceCenter(i); + mesh.sheet(i).vorticitySmoothed = mesh.sheet(i).vorticity; + } + for(int i=0; i<mesh.numTris(); i++) { + for (int c=0; c<3; c++) { + int oc = mesh.corners(i,c).opposite; + if (oc>=0) { + int t = mesh.corners(oc).tri; + weights[3*i+c] = exp(normSquare(pos[t]-pos[i])*mult); + index[3*i+c] = t; + } + else { + weights[3*i+c] = 0; + index[3*i+c] = 0; + } + } + } + + for (int it=0; it<iter; ++it) { + // first, preload + for(int i=0; i<mesh.numTris(); i++) vort[i] = mesh.sheet(i).vorticitySmoothed; + + for(int i=0,idx=0; i<mesh.numTris(); i++) { + // loop over adjacent tris + Real sum=1.0f; + Vec3 v=vort[i]; + for (int c=0;c<3;c++,idx++) { + Real w = weights[index[idx]]; + v += w*vort[index[idx]]; + sum += w; + } + mesh.sheet(i).vorticitySmoothed = v/sum; + } + } + for(int i=0; i<mesh.numTris(); i++) mesh.sheet(i).vorticitySmoothed *= alpha; +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "smoothVorticity" ); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh& mesh = *_args.getPtr<VortexSheetMesh >("mesh",0,&_lock); int iter = _args.getOpt<int >("iter",1,1,&_lock); Real sigma = _args.getOpt<Real >("sigma",2,0.2,&_lock); Real alpha = _args.getOpt<Real >("alpha",3,0.8,&_lock); _retval = getPyNone(); smoothVorticity(mesh,iter,sigma,alpha); _args.check(); } pbFinalizePlugin(parent,"smoothVorticity" ); return _retval; } catch(std::exception& e) { pbSetError("smoothVorticity",e.what()); return 0; } } static const Pb::Register _RP_smoothVorticity ("","smoothVorticity",_W_4); + +//! Seed Vortex Particles inside shape with K41 characteristics +void VPseedK41(VortexParticleSystem& system, Shape* shape, Real strength=0, Real sigma0=0.2, Real sigma1=1.0, Real probability=1.0, Real N=3.0) { + Grid<Real> temp(system.getParent()); + const Real dt = system.getParent()->getDt(); + static RandomStream rand(3489572); + Real s0 = pow( (Real)sigma0, (Real)(-N+1.0) ); + Real s1 = pow( (Real)sigma1, (Real)(-N+1.0) ); + + FOR_IJK(temp) { + if (shape->isInsideGrid(i,j,k)) { + if (rand.getReal() < probability*dt) { + Real p = rand.getReal(); + Real sigma = pow( (1.0-p)*s0 + p*s1, 1./(-N+1.0) ); + Vec3 randDir (rand.getReal(), rand.getReal(), rand.getReal()); + Vec3 posUpd (i+rand.getReal(), j+rand.getReal(), k+rand.getReal()); + normalize(randDir); + Vec3 vorticity = randDir * strength * pow( (Real)sigma, (Real)(-10./6.+N/2.0) ); + system.add(VortexParticleData(posUpd, vorticity, sigma)); + } + } + } +} static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "VPseedK41" ); PyObject *_retval = 0; { ArgLocker _lock; VortexParticleSystem& system = *_args.getPtr<VortexParticleSystem >("system",0,&_lock); Shape* shape = _args.getPtr<Shape >("shape",1,&_lock); Real strength = _args.getOpt<Real >("strength",2,0,&_lock); Real sigma0 = _args.getOpt<Real >("sigma0",3,0.2,&_lock); Real sigma1 = _args.getOpt<Real >("sigma1",4,1.0,&_lock); Real probability = _args.getOpt<Real >("probability",5,1.0,&_lock); Real N = _args.getOpt<Real >("N",6,3.0,&_lock); _retval = getPyNone(); VPseedK41(system,shape,strength,sigma0,sigma1,probability,N); _args.check(); } pbFinalizePlugin(parent,"VPseedK41" ); return _retval; } catch(std::exception& e) { pbSetError("VPseedK41",e.what()); return 0; } } static const Pb::Register _RP_VPseedK41 ("","VPseedK41",_W_5); + +//! Vortex-in-cell integration + +void VICintegration(VortexSheetMesh& mesh, Real sigma, Grid<Vec3>& vel, FlagGrid& flags, Grid<Vec3>* vorticity=NULL, Real cgMaxIterFac=1.5, Real cgAccuracy=1e-3, Real scale = 0.01, int precondition=0) { + + MuTime t0; + const Real fac = 16.0; // experimental factor to balance out regularization + + // if no vort grid is given, use a temporary one + Grid<Vec3> vortTemp(mesh.getParent()); + Grid<Vec3>& vort = (vorticity) ? (*vorticity) : (vortTemp); + vort.clear(); + + // map vorticity to grid using Peskin kernel + int sgi = ceil(sigma); + Real pkfac=M_PI/sigma; + const int numTris = mesh.numTris(); + for (int t=0; t<numTris; t++) { + Vec3 pos = mesh.getFaceCenter(t); + Vec3 v = mesh.sheet(t).vorticity * mesh.getFaceArea(t) * fac; + + // inner kernel + // first, summate + Real sum=0; + for (int i=-sgi; i<sgi; i++) { + if (pos.x+i < 0 || (int)pos.x+i >= vort.getSizeX()) continue; + for (int j=-sgi; j<sgi; j++) { + if (pos.y+j < 0 || (int)pos.y+j >= vort.getSizeY()) continue; + for (int k=-sgi; k<sgi; k++) { + if (pos.z+k < 0 || (int)pos.z+k >= vort.getSizeZ()) continue; + Vec3i cell(pos.x+i, pos.y+j, pos.z+k); + if (!flags.isFluid(cell)) continue; + Vec3 d = pos - Vec3(i+0.5+floor(pos.x), j+0.5+floor(pos.y), k+0.5+floor(pos.z)); + Real dl = norm(d); + if (dl > sigma) continue; + // precalc Peskin kernel + sum += 1.0 + cos(dl * pkfac); + } + } + } + // then, apply normalized kernel + Real wnorm = 1.0/sum; + for (int i=-sgi; i<sgi; i++) { + if (pos.x+i < 0 || (int)pos.x+i >= vort.getSizeX()) continue; + for (int j=-sgi; j<sgi; j++) { + if (pos.y+j < 0 || (int)pos.y+j >= vort.getSizeY()) continue; + for (int k=-sgi; k<sgi; k++) { + if (pos.z+k < 0 || (int)pos.z+k >= vort.getSizeZ()) continue; + Vec3i cell(pos.x+i, pos.y+j, pos.z+k); + if (!flags.isFluid(cell)) continue; + Vec3 d = pos - Vec3(i+0.5+floor(pos.x), j+0.5+floor(pos.y), k+0.5+floor(pos.z)); + Real dl = norm(d); + if (dl > sigma) continue; + Real w = (1.0 + cos(dl * pkfac))*wnorm; + vort(cell) += v * w; + } + } + } + } + + // Prepare grids for poisson solve + Grid<Vec3> vortexCurl(mesh.getParent()); + Grid<Real> rhs(mesh.getParent()); + Grid<Real> solution(mesh.getParent()); + Grid<Real> residual(mesh.getParent()); + Grid<Real> search(mesh.getParent()); + Grid<Real> temp1(mesh.getParent()); + Grid<Real> A0(mesh.getParent()); + Grid<Real> Ai(mesh.getParent()); + Grid<Real> Aj(mesh.getParent()); + Grid<Real> Ak(mesh.getParent()); + Grid<Real> pca0(mesh.getParent()); + Grid<Real> pca1(mesh.getParent()); + Grid<Real> pca2(mesh.getParent()); + Grid<Real> pca3(mesh.getParent()); + + MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak); + CurlOp(vort, vortexCurl); + + // Solve vector poisson equation + for (int c=0; c<3; c++) { + // construct rhs + if (vel.getType() & GridBase::TypeMAC) + GetShiftedComponent(vortexCurl, rhs, c); + else + GetComponent(vortexCurl, rhs, c); + + // prepare CG solver + const int maxIter = (int)(cgMaxIterFac * vel.getSize().max()); + GridCgInterface *gcg = new GridCg<ApplyMatrix>(solution, rhs, residual, search, flags, temp1, &A0, &Ai, &Aj, &Ak ); + gcg->setAccuracy(cgAccuracy); + gcg->setUseResNorm(true); + gcg->setPreconditioner( (GridCgInterface::PreconditionType)precondition, &pca0, &pca1, &pca2, &pca3); + + // iterations + for (int iter=0; iter<maxIter; iter++) { + if (!gcg->iterate()) iter=maxIter; + } + debMsg("VICintegration CG iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1); + delete gcg; + + // copy back + solution *= scale; + SetComponent(vel, solution, c); + } +} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "VICintegration" ); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh& mesh = *_args.getPtr<VortexSheetMesh >("mesh",0,&_lock); Real sigma = _args.get<Real >("sigma",1,&_lock); Grid<Vec3>& vel = *_args.getPtr<Grid<Vec3> >("vel",2,&_lock); FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",3,&_lock); Grid<Vec3>* vorticity = _args.getPtrOpt<Grid<Vec3> >("vorticity",4,NULL,&_lock); Real cgMaxIterFac = _args.getOpt<Real >("cgMaxIterFac",5,1.5,&_lock); Real cgAccuracy = _args.getOpt<Real >("cgAccuracy",6,1e-3,&_lock); Real scale = _args.getOpt<Real >("scale",7,0.01,&_lock); int precondition = _args.getOpt<int >("precondition",8,0,&_lock); _retval = getPyNone(); VICintegration(mesh,sigma,vel,flags,vorticity,cgMaxIterFac,cgAccuracy,scale,precondition); _args.check(); } pbFinalizePlugin(parent,"VICintegration" ); return _retval; } catch(std::exception& e) { pbSetError("VICintegration",e.what()); return 0; } } static const Pb::Register _RP_VICintegration ("","VICintegration",_W_6); + +//! Obtain density field from levelset with linear gradient of size sigma over the interface +void densityFromLevelset(LevelsetGrid& phi, Grid<Real>& density, Real value=1.0, Real sigma=1.0) { + FOR_IJK(phi) { + // remove boundary + if (i<2 || j<2 || k<2 || i>=phi.getSizeX()-2 || j>=phi.getSizeY()-2 || k>=phi.getSizeZ()-2) + density(i,j,k) = 0; + else if (phi(i,j,k) < -sigma) + density(i,j,k) = value; + else if (phi(i,j,k) > sigma) + density(i,j,k) = 0; + else + density(i,j,k) = clamp((Real)(0.5*value/sigma*(1.0-phi(i,j,k))), _0, value); + } +} static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "densityFromLevelset" ); PyObject *_retval = 0; { ArgLocker _lock; LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",0,&_lock); Grid<Real>& density = *_args.getPtr<Grid<Real> >("density",1,&_lock); Real value = _args.getOpt<Real >("value",2,1.0,&_lock); Real sigma = _args.getOpt<Real >("sigma",3,1.0,&_lock); _retval = getPyNone(); densityFromLevelset(phi,density,value,sigma); _args.check(); } pbFinalizePlugin(parent,"densityFromLevelset" ); return _retval; } catch(std::exception& e) { pbSetError("densityFromLevelset",e.what()); return 0; } } static const Pb::Register _RP_densityFromLevelset ("","densityFromLevelset",_W_7); + +} // namespace + diff --git a/source/blender/python/manta_pp/plugin/waveletturbulence.cpp b/source/blender/python/manta_pp/plugin/waveletturbulence.cpp new file mode 100644 index 00000000000..819fd35fb6f --- /dev/null +++ b/source/blender/python/manta_pp/plugin/waveletturbulence.cpp @@ -0,0 +1,309 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Functions for calculating wavelet turbulence, + * plus helpers to compute vorticity, and strain rate magnitude + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "noisefield.h" + +using namespace std; + +namespace Manta { + + +//! Apply vector noise to grid, this is a simplified version - no position scaling or UVs + + + + struct knApplySimpleNoiseVec : public KernelBase { knApplySimpleNoiseVec(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale, Grid<Real>* weight ) : KernelBase(&flags,0) ,flags(flags),target(target),noise(noise),scale(scale),weight(weight) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale, Grid<Real>* weight ) { + if ( !flags.isFluid(i,j,k) ) return; + Real factor = 1; + if(weight) factor = (*weight)(i,j,k); + target(i,j,k) += noise.evaluateCurl( Vec3(i,j,k) ) * scale * factor; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Vec3>& getArg1() { return target; } typedef Grid<Vec3> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Real& getArg3() { return scale; } typedef Real type3;inline Grid<Real>* getArg4() { return weight; } typedef Grid<Real> type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,target,noise,scale,weight); } FlagGrid& flags; Grid<Vec3>& target; WaveletNoiseField& noise; Real scale; Grid<Real>* weight; }; + + +void applySimpleNoiseVec3(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale=1.0 , Grid<Real>* weight=NULL ) { + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component separately + knApplySimpleNoiseVec(flags, target, noise, scale , weight ); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "applySimpleNoiseVec3" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Vec3>& target = *_args.getPtr<Grid<Vec3> >("target",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.0 ,&_lock); Grid<Real>* weight = _args.getPtrOpt<Grid<Real> >("weight",4,NULL ,&_lock); _retval = getPyNone(); applySimpleNoiseVec3(flags,target,noise,scale,weight); _args.check(); } pbFinalizePlugin(parent,"applySimpleNoiseVec3" ); return _retval; } catch(std::exception& e) { pbSetError("applySimpleNoiseVec3",e.what()); return 0; } } static const Pb::Register _RP_applySimpleNoiseVec3 ("","applySimpleNoiseVec3",_W_0); + + +//! Simple noise for a real grid , follows applySimpleNoiseVec3 + + + + struct knApplySimpleNoiseReal : public KernelBase { knApplySimpleNoiseReal(FlagGrid& flags, Grid<Real>& target, WaveletNoiseField& noise, Real scale, Grid<Real>* weight ) : KernelBase(&flags,0) ,flags(flags),target(target),noise(noise),scale(scale),weight(weight) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& target, WaveletNoiseField& noise, Real scale, Grid<Real>* weight ) { + if ( !flags.isFluid(i,j,k) ) return; + Real factor = 1; + if(weight) factor = (*weight)(i,j,k); + target(i,j,k) += noise.evaluate( Vec3(i,j,k) ) * scale * factor; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return target; } typedef Grid<Real> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Real& getArg3() { return scale; } typedef Real type3;inline Grid<Real>* getArg4() { return weight; } typedef Grid<Real> type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,target,noise,scale,weight); } FlagGrid& flags; Grid<Real>& target; WaveletNoiseField& noise; Real scale; Grid<Real>* weight; }; + + +void applySimpleNoiseReal(FlagGrid& flags, Grid<Real>& target, WaveletNoiseField& noise, Real scale=1.0 , Grid<Real>* weight=NULL ) { + knApplySimpleNoiseReal(flags, target, noise, scale , weight ); +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "applySimpleNoiseReal" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& target = *_args.getPtr<Grid<Real> >("target",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.0 ,&_lock); Grid<Real>* weight = _args.getPtrOpt<Grid<Real> >("weight",4,NULL ,&_lock); _retval = getPyNone(); applySimpleNoiseReal(flags,target,noise,scale,weight); _args.check(); } pbFinalizePlugin(parent,"applySimpleNoiseReal" ); return _retval; } catch(std::exception& e) { pbSetError("applySimpleNoiseReal",e.what()); return 0; } } static const Pb::Register _RP_applySimpleNoiseReal ("","applySimpleNoiseReal",_W_1); + + + +//! Apply vector-based wavelet noise to target grid +//! This is the version with more functionality - supports uv grids, and on-the-fly interpolation +//! of input grids. + + + + struct knApplyNoiseVec : public KernelBase { knApplyNoiseVec(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale, Real scaleSpatial, Grid<Real>* weight, Grid<Vec3>* uv, bool uvInterpol, const Vec3& sourceFactor ) : KernelBase(&flags,0) ,flags(flags),target(target),noise(noise),scale(scale),scaleSpatial(scaleSpatial),weight(weight),uv(uv),uvInterpol(uvInterpol),sourceFactor(sourceFactor) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale, Real scaleSpatial, Grid<Real>* weight, Grid<Vec3>* uv, bool uvInterpol, const Vec3& sourceFactor ) { + if ( !flags.isFluid(i,j,k) ) return; + + // get weighting, interpolate if necessary + Real w = 1; + if(weight) { + if(!uvInterpol) { + w = (*weight)(i,j,k); + } else { + w = weight->getInterpolated( Vec3(i,j,k) * sourceFactor ); + } + } + + // compute position where to evaluate the noise + Vec3 pos = Vec3(i,j,k); + if(uv) { + if(!uvInterpol) { + pos = (*uv)(i,j,k); + } else { + pos = uv->getInterpolated( Vec3(i,j,k) * sourceFactor ); + // uv coordinates are in local space - so we need to adjust the values of the positions + pos /= sourceFactor; + } + } + pos *= scaleSpatial; + + Vec3 noiseVec3 = noise.evaluateCurl( pos ) * scale * w; + //noiseVec3=pos; // debug , show interpolated positions + target(i,j,k) += noiseVec3; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Vec3>& getArg1() { return target; } typedef Grid<Vec3> type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Real& getArg3() { return scale; } typedef Real type3;inline Real& getArg4() { return scaleSpatial; } typedef Real type4;inline Grid<Real>* getArg5() { return weight; } typedef Grid<Real> type5;inline Grid<Vec3>* getArg6() { return uv; } typedef Grid<Vec3> type6;inline bool& getArg7() { return uvInterpol; } typedef bool type7;inline const Vec3& getArg8() { return sourceFactor; } typedef Vec3 type8; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,target,noise,scale,scaleSpatial,weight,uv,uvInterpol,sourceFactor); } FlagGrid& flags; Grid<Vec3>& target; WaveletNoiseField& noise; Real scale; Real scaleSpatial; Grid<Real>* weight; Grid<Vec3>* uv; bool uvInterpol; const Vec3& sourceFactor; }; + + +void applyNoiseVec3(FlagGrid& flags, Grid<Vec3>& target, WaveletNoiseField& noise, Real scale=1.0 , Real scaleSpatial=1.0 , Grid<Real>* weight=NULL , Grid<Vec3>* uv=NULL ) { + // check whether the uv grid has a different resolution + bool uvInterpol = false; + // and pre-compute conversion (only used if uvInterpol==true) + // used for both uv and weight grid... + Vec3 sourceFactor = Vec3(1.); + if(uv) { + uvInterpol = (target.getSize() != uv->getSize()); + sourceFactor = calcGridSizeFactor( uv->getSize(), target.getSize() ); + } else if(weight) { + uvInterpol = (target.getSize() != weight->getSize()); + sourceFactor = calcGridSizeFactor( weight->getSize(), target.getSize() ); + } + if(uv && weight) assertMsg( uv->getSize() == weight->getSize(), "UV and weight grid have to match!"); + + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component separately + knApplyNoiseVec(flags, target, noise, scale, scaleSpatial, weight , uv,uvInterpol,sourceFactor ); +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "applyNoiseVec3" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Vec3>& target = *_args.getPtr<Grid<Vec3> >("target",1,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",2,&_lock); Real scale = _args.getOpt<Real >("scale",3,1.0 ,&_lock); Real scaleSpatial = _args.getOpt<Real >("scaleSpatial",4,1.0 ,&_lock); Grid<Real>* weight = _args.getPtrOpt<Grid<Real> >("weight",5,NULL ,&_lock); Grid<Vec3>* uv = _args.getPtrOpt<Grid<Vec3> >("uv",6,NULL ,&_lock); _retval = getPyNone(); applyNoiseVec3(flags,target,noise,scale,scaleSpatial,weight,uv); _args.check(); } pbFinalizePlugin(parent,"applyNoiseVec3" ); return _retval; } catch(std::exception& e) { pbSetError("applyNoiseVec3",e.what()); return 0; } } static const Pb::Register _RP_applyNoiseVec3 ("","applyNoiseVec3",_W_2); + + + +//! Compute energy of a staggered velocity field (at cell center) + + + struct KnApplyComputeEnergy : public KernelBase { KnApplyComputeEnergy( FlagGrid& flags, MACGrid& vel, Grid<Real>& energy ) : KernelBase(&flags,0) ,flags(flags),vel(vel),energy(energy) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, MACGrid& vel, Grid<Real>& energy ) { + Real e = 0.f; + if ( flags.isFluid(i,j,k) ) { + Vec3 v = vel.getCentered(i,j,k); + e = 0.5 * v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + } + energy(i,j,k) = e; +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline MACGrid& getArg1() { return vel; } typedef MACGrid type1;inline Grid<Real>& getArg2() { return energy; } typedef Grid<Real> type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, flags,vel,energy); } FlagGrid& flags; MACGrid& vel; Grid<Real>& energy; }; + + +void computeEnergy( FlagGrid& flags, MACGrid& vel, Grid<Real>& energy ) { + KnApplyComputeEnergy( flags, vel, energy ); +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "computeEnergy" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); Grid<Real>& energy = *_args.getPtr<Grid<Real> >("energy",2,&_lock); _retval = getPyNone(); computeEnergy(flags,vel,energy); _args.check(); } pbFinalizePlugin(parent,"computeEnergy" ); return _retval; } catch(std::exception& e) { pbSetError("computeEnergy",e.what()); return 0; } } static const Pb::Register _RP_computeEnergy ("","computeEnergy",_W_3); + + + +//!interpolate grid from one size to another size + + + struct KnInterpolateGrid : public KernelBase { KnInterpolateGrid(Grid<Real>& target, Grid<Real>& source, const Vec3& sourceFactor) : KernelBase(&target,0) ,target(target),source(source),sourceFactor(sourceFactor) { run(); } inline void op(int i, int j, int k, Grid<Real>& target, Grid<Real>& source, const Vec3& sourceFactor ) { + Vec3 pos = Vec3(i,j,k) * sourceFactor; + if(!source.is3D()) pos[2] = 0; // allow 2d -> 3d + target(i,j,k) = source.getInterpolated(pos); +} inline Grid<Real>& getArg0() { return target; } typedef Grid<Real> type0;inline Grid<Real>& getArg1() { return source; } typedef Grid<Real> type1;inline const Vec3& getArg2() { return sourceFactor; } typedef Vec3 type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, target,source,sourceFactor); } Grid<Real>& target; Grid<Real>& source; const Vec3& sourceFactor; }; + + +void interpolateGrid( Grid<Real>& target, Grid<Real>& source ) { + Vec3 sourceFactor = calcGridSizeFactor( source.getSize(), target.getSize() ); + + // a brief note on a mantaflow specialty: the target grid has to be the first argument here! + // the parent fluidsolver object is taken from the first grid, and it determines the size of the + // loop for the kernel call. as we're writing into target, it's important to loop exactly over + // all cells of the target grid... (note, when calling the plugin in python, it doesnt matter anymore). + + KnInterpolateGrid(target, source, sourceFactor); +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "interpolateGrid" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& target = *_args.getPtr<Grid<Real> >("target",0,&_lock); Grid<Real>& source = *_args.getPtr<Grid<Real> >("source",1,&_lock); _retval = getPyNone(); interpolateGrid(target,source); _args.check(); } pbFinalizePlugin(parent,"interpolateGrid" ); return _retval; } catch(std::exception& e) { pbSetError("interpolateGrid",e.what()); return 0; } } static const Pb::Register _RP_interpolateGrid ("","interpolateGrid",_W_4); + + +//!interpolate a mac velocity grid from one size to another size + + + struct KnInterpolateMACGrid : public KernelBase { KnInterpolateMACGrid(MACGrid& target, MACGrid& source, const Vec3& sourceFactor) : KernelBase(&target,0) ,target(target),source(source),sourceFactor(sourceFactor) { run(); } inline void op(int i, int j, int k, MACGrid& target, MACGrid& source, const Vec3& sourceFactor ) { + Vec3 pos = Vec3(i,j,k) * sourceFactor; + + Real vx = source.getInterpolated(pos - Vec3(0.5,0,0))[0]; + Real vy = source.getInterpolated(pos - Vec3(0,0.5,0))[1]; + Real vz = 0.f; + if(source.is3D()) vz = source.getInterpolated(pos - Vec3(0,0,0.5))[2]; + + target(i,j,k) = Vec3(vx,vy,vz); +} inline MACGrid& getArg0() { return target; } typedef MACGrid type0;inline MACGrid& getArg1() { return source; } typedef MACGrid type1;inline const Vec3& getArg2() { return sourceFactor; } typedef Vec3 type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, target,source,sourceFactor); } MACGrid& target; MACGrid& source; const Vec3& sourceFactor; }; + + +void interpolateMACGrid(MACGrid& target, MACGrid& source) { + Vec3 sourceFactor = calcGridSizeFactor( source.getSize(), target.getSize() ); + + // see interpolateGrid for why the target grid needs to come first in the parameters! + + KnInterpolateMACGrid(target, source, sourceFactor); +} static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "interpolateMACGrid" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& target = *_args.getPtr<MACGrid >("target",0,&_lock); MACGrid& source = *_args.getPtr<MACGrid >("source",1,&_lock); _retval = getPyNone(); interpolateMACGrid(target,source); _args.check(); } pbFinalizePlugin(parent,"interpolateMACGrid" ); return _retval; } catch(std::exception& e) { pbSetError("interpolateMACGrid",e.what()); return 0; } } static const Pb::Register _RP_interpolateMACGrid ("","interpolateMACGrid",_W_5); + + +void computeWaveletCoeffs(Grid<Real>& input) { + Grid<Real> temp1(input.getParent()), temp2(input.getParent()); + WaveletNoiseField::computeCoefficients(input, temp1, temp2); +} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "computeWaveletCoeffs" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& input = *_args.getPtr<Grid<Real> >("input",0,&_lock); _retval = getPyNone(); computeWaveletCoeffs(input); _args.check(); } pbFinalizePlugin(parent,"computeWaveletCoeffs" ); return _retval; } catch(std::exception& e) { pbSetError("computeWaveletCoeffs",e.what()); return 0; } } static const Pb::Register _RP_computeWaveletCoeffs ("","computeWaveletCoeffs",_W_6); + +// note - alomst the same as for vorticity confinement +void computeVorticity(MACGrid& vel, Grid<Vec3>& vorticity, Grid<Real>* norm) { + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + CurlOp(velCenter, vorticity); + if(norm) GridNorm( *norm, vorticity); +} static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "computeVorticity" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Vec3>& vorticity = *_args.getPtr<Grid<Vec3> >("vorticity",1,&_lock); Grid<Real>* norm = _args.getPtr<Grid<Real> >("norm",2,&_lock); _retval = getPyNone(); computeVorticity(vel,vorticity,norm); _args.check(); } pbFinalizePlugin(parent,"computeVorticity" ); return _retval; } catch(std::exception& e) { pbSetError("computeVorticity",e.what()); return 0; } } static const Pb::Register _RP_computeVorticity ("","computeVorticity",_W_7); + +// note - very similar to KnComputeProductionStrain, but for use as wavelet turb weighting + + + struct KnComputeStrainRateMag : public KernelBase { KnComputeStrainRateMag(const MACGrid& vel, const Grid<Vec3>& velCenter, Grid<Real>& prod ) : KernelBase(&vel,1) ,vel(vel),velCenter(velCenter),prod(prod) { run(); } inline void op(int i, int j, int k, const MACGrid& vel, const Grid<Vec3>& velCenter, Grid<Real>& prod ) { + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i+1,j,k).x, vel(i,j+1,k).y, 0. ) - vel(i,j,k); + if(vel.is3D()) diag[2] += vel(i,j,k+1).z; + else diag[2] = 0.; + + Vec3 ux = 0.5*(velCenter(i+1,j,k)-velCenter(i-1,j,k)); + Vec3 uy = 0.5*(velCenter(i,j+1,k)-velCenter(i,j-1,k)); + Vec3 uz; + if(vel.is3D()) uz=0.5*(velCenter(i,j,k+1)-velCenter(i,j,k-1)); + + Real S12 = 0.5*(ux.y+uy.x); + Real S13 = 0.5*(ux.z+uz.x); + Real S23 = 0.5*(uy.z+uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + + 2.0*square(S12) + 2.0*square(S13) + 2.0*square(S23); + prod(i,j,k) = S2; +} inline const MACGrid& getArg0() { return vel; } typedef MACGrid type0;inline const Grid<Vec3>& getArg1() { return velCenter; } typedef Grid<Vec3> type1;inline Grid<Real>& getArg2() { return prod; } typedef Grid<Real> type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, vel,velCenter,prod); } const MACGrid& vel; const Grid<Vec3>& velCenter; Grid<Real>& prod; }; +void computeStrainRateMag(MACGrid& vel, Grid<Real>& mag) { + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + KnComputeStrainRateMag(vel, velCenter, mag); +} static PyObject* _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "computeStrainRateMag" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Real>& mag = *_args.getPtr<Grid<Real> >("mag",1,&_lock); _retval = getPyNone(); computeStrainRateMag(vel,mag); _args.check(); } pbFinalizePlugin(parent,"computeStrainRateMag" ); return _retval; } catch(std::exception& e) { pbSetError("computeStrainRateMag",e.what()); return 0; } } static const Pb::Register _RP_computeStrainRateMag ("","computeStrainRateMag",_W_8); + + +// extrapolate a real grid into a flagged region (based on initial flags) +// by default extrapolates from fluid to obstacle cells +template<class T> +void extrapolSimpleFlagsHelper (FlagGrid& flags, Grid<T>& val, int distance = 4, + int flagFrom=FlagGrid::TypeFluid, int flagTo=FlagGrid::TypeObstacle ) +{ + Grid<int> tmp( flags.getParent() ); + int dim = (flags.is3D() ? 3:2); + const Vec3i nb[6] = { + Vec3i(1 ,0,0), Vec3i(-1,0,0), + Vec3i(0,1 ,0), Vec3i(0,-1,0), + Vec3i(0,0,1 ), Vec3i(0,0,-1) }; + + // remove all fluid cells (set to 1) + tmp.clear(); + bool foundTarget = false; + FOR_IJK_BND(flags,0) { + if (flags(i,j,k) & flagFrom) + tmp( Vec3i(i,j,k) ) = 1; + if (!foundTarget && (flags(i,j,k) & flagTo)) foundTarget=true; + } + // optimization, skip extrapolation if we dont have any cells to extrapolate to + if(!foundTarget) { + debMsg("No target cells found, skipping extrapolation", 1); + return; + } + + // extrapolate for given distance + for(int d=1; d<1+distance; ++d) { + + // TODO, parallelize + FOR_IJK_BND(flags,1) { + if (tmp(i,j,k) != 0) continue; + if (!(flags(i,j,k) & flagTo)) continue; + + // copy from initialized neighbors + Vec3i p(i,j,k); + int nbs = 0; + T avgVal = 0.; + for (int n=0; n<2*dim; ++n) { + if (tmp(p+nb[n]) == d) { + avgVal += val(p+nb[n]); + nbs++; + } + } + + if(nbs>0) { + tmp(p) = d+1; + val(p) = avgVal / nbs; + } + } + + } // distance +} + + +void extrapolateSimpleFlags(FlagGrid& flags, GridBase* val, int distance = 4, int flagFrom=FlagGrid::TypeFluid, int flagTo=FlagGrid::TypeObstacle ) { + if (val->getType() & GridBase::TypeReal) { + extrapolSimpleFlagsHelper<Real>(flags,*((Grid<Real>*) val),distance,flagFrom,flagTo); + } + else if (val->getType() & GridBase::TypeInt) { + extrapolSimpleFlagsHelper<int >(flags,*((Grid<int >*) val),distance,flagFrom,flagTo); + } + else if (val->getType() & GridBase::TypeVec3) { + extrapolSimpleFlagsHelper<Vec3>(flags,*((Grid<Vec3>*) val),distance,flagFrom,flagTo); + } + else + errMsg("extrapolateSimpleFlags: Grid Type is not supported (only int, Real, Vec3)"); +} static PyObject* _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "extrapolateSimpleFlags" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); GridBase* val = _args.getPtr<GridBase >("val",1,&_lock); int distance = _args.getOpt<int >("distance",2,4,&_lock); int flagFrom = _args.getOpt<int >("flagFrom",3,FlagGrid::TypeFluid,&_lock); int flagTo = _args.getOpt<int >("flagTo",4,FlagGrid::TypeObstacle ,&_lock); _retval = getPyNone(); extrapolateSimpleFlags(flags,val,distance,flagFrom,flagTo); _args.check(); } pbFinalizePlugin(parent,"extrapolateSimpleFlags" ); return _retval; } catch(std::exception& e) { pbSetError("extrapolateSimpleFlags",e.what()); return 0; } } static const Pb::Register _RP_extrapolateSimpleFlags ("","extrapolateSimpleFlags",_W_9); + +} // namespace + + diff --git a/source/blender/python/manta_pp/plugin/waves.cpp b/source/blender/python/manta_pp/plugin/waves.cpp new file mode 100644 index 00000000000..0181b3de5f2 --- /dev/null +++ b/source/blender/python/manta_pp/plugin/waves.cpp @@ -0,0 +1,174 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Wave equation + * + ******************************************************************************/ + +#include "levelset.h" +#include "commonkernels.h" +#include "particle.h" +#include "conjugategrad.h" +#include <cmath> + +using namespace std; + +namespace Manta { + + +/****************************************************************************** + * + * explicit integration + * + ******************************************************************************/ + + + + struct knCalcSecDeriv2d : public KernelBase { knCalcSecDeriv2d(const Grid<Real>& v, Grid<Real>& ret) : KernelBase(&v,1) ,v(v),ret(ret) { run(); } inline void op(int i, int j, int k, const Grid<Real>& v, Grid<Real>& ret ) { + + ret(i,j,k) = + ( -4. * v(i,j,k) + v(i-1,j,k) + v(i+1,j,k) + v(i,j-1,k) + v(i,j+1,k) ); + +} inline const Grid<Real>& getArg0() { return v; } typedef Grid<Real> type0;inline Grid<Real>& getArg1() { return ret; } typedef Grid<Real> type1; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, v,ret); } const Grid<Real>& v; Grid<Real>& ret; };; + + +void calcSecDeriv2d(const Grid<Real>& v, Grid<Real>& curv) { + knCalcSecDeriv2d(v,curv); +} static PyObject* _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "calcSecDeriv2d" ); PyObject *_retval = 0; { ArgLocker _lock; const Grid<Real>& v = *_args.getPtr<Grid<Real> >("v",0,&_lock); Grid<Real>& curv = *_args.getPtr<Grid<Real> >("curv",1,&_lock); _retval = getPyNone(); calcSecDeriv2d(v,curv); _args.check(); } pbFinalizePlugin(parent,"calcSecDeriv2d" ); return _retval; } catch(std::exception& e) { pbSetError("calcSecDeriv2d",e.what()); return 0; } } static const Pb::Register _RP_calcSecDeriv2d ("","calcSecDeriv2d",_W_0); + + +// mass conservation + + + struct knTotalSum : public KernelBase { knTotalSum(Grid<Real>& h) : KernelBase(&h,1) ,h(h) ,sum(0) { run(); } inline void op(int i, int j, int k, Grid<Real>& h ,double& sum) { sum += h(i,j,k); } inline Grid<Real>& getArg0() { return h; } typedef Grid<Real> type0; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, h,sum); } Grid<Real>& h; double sum; }; + +Real totalSum(Grid<Real>& height) { + knTotalSum ts(height); + return ts.sum; +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "totalSum" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& height = *_args.getPtr<Grid<Real> >("height",0,&_lock); _retval = toPy(totalSum(height)); _args.check(); } pbFinalizePlugin(parent,"totalSum" ); return _retval; } catch(std::exception& e) { pbSetError("totalSum",e.what()); return 0; } } static const Pb::Register _RP_totalSum ("","totalSum",_W_1); + +void normalizeSumTo(Grid<Real>& height, Real target) { + knTotalSum ts(height); + Real factor = target / ts.sum; + height.multConst(factor); +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "normalizeSumTo" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<Real>& height = *_args.getPtr<Grid<Real> >("height",0,&_lock); Real target = _args.get<Real >("target",1,&_lock); _retval = getPyNone(); normalizeSumTo(height,target); _args.check(); } pbFinalizePlugin(parent,"normalizeSumTo" ); return _retval; } catch(std::exception& e) { pbSetError("normalizeSumTo",e.what()); return 0; } } static const Pb::Register _RP_normalizeSumTo ("","normalizeSumTo",_W_2); + + +/****************************************************************************** + * + * implicit time integration + * + ******************************************************************************/ + + + +//! Kernel: Construct the right-hand side of the poisson equation + + + + struct MakeRhsWE : public KernelBase { MakeRhsWE(FlagGrid& flags, Grid<Real>& rhs, Grid<Real>& ut, Grid<Real>& utm1, Real s, bool crankNic=false) : KernelBase(&flags,1) ,flags(flags),rhs(rhs),ut(ut),utm1(utm1),s(s),crankNic(crankNic) { run(); } inline void op(int i, int j, int k, FlagGrid& flags, Grid<Real>& rhs, Grid<Real>& ut, Grid<Real>& utm1, Real s, bool crankNic=false ) { + rhs(i,j,k) = ( 2.*ut(i,j,k) - utm1(i,j,k) ); + if(crankNic) { + rhs(i,j,k) += s * ( -4.*ut(i,j,k) + 1.*ut(i-1,j,k) + 1.*ut(i+1,j,k) + 1.*ut(i,j-1,k) + 1.*ut(i,j+1,k) ); + } +} inline FlagGrid& getArg0() { return flags; } typedef FlagGrid type0;inline Grid<Real>& getArg1() { return rhs; } typedef Grid<Real> type1;inline Grid<Real>& getArg2() { return ut; } typedef Grid<Real> type2;inline Grid<Real>& getArg3() { return utm1; } typedef Grid<Real> type3;inline Real& getArg4() { return s; } typedef Real type4;inline bool& getArg5() { return crankNic; } typedef bool type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=1; j< _maxY; j++) for (int i=1; i< _maxX; i++) op(i,j,k, flags,rhs,ut,utm1,s,crankNic); } FlagGrid& flags; Grid<Real>& rhs; Grid<Real>& ut; Grid<Real>& utm1; Real s; bool crankNic; }; + + + + + +//! do a CG solve (note, out grid only there for debugging... could be removed) + + + + + +void cgSolveWE(FlagGrid& flags, Grid<Real>& ut, Grid<Real>& utm1, Grid<Real>& out, bool crankNic = false, Real cSqr = 0.25, Real cgMaxIterFac = 1.5, Real cgAccuracy = 1e-5 ) { + // reserve temp grids + FluidSolver* parent = flags.getParent(); + Grid<Real> rhs(parent); + Grid<Real> residual(parent); + Grid<Real> search(parent); + Grid<Real> A0(parent); + Grid<Real> Ai(parent); + Grid<Real> Aj(parent); + Grid<Real> Ak(parent); + Grid<Real> tmp(parent); + //Grid<Real> pca0(parent); + //Grid<Real> pca1(parent); + //Grid<Real> pca2(parent); + //Grid<Real> pca3(parent); + // solution... + //Grid<Real> pressure(parent); + out.clear(); + + // setup matrix and boundaries + MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak); + Real dt = parent->getDt(); + Real s = dt*dt*cSqr * 0.5; + FOR_IJK(flags) { + Ai(i,j,k) *= s; + Aj(i,j,k) *= s; + Ak(i,j,k) *= s; + A0(i,j,k) *= s; + A0(i,j,k) += 1.; + } + + // compute divergence and init right hand side + rhs.clear(); + // h=dt + // rhs: = 2 ut - ut-1 + // A: (h2 c2/ dx)=s , (1+4s)uij + s ui-1j + ... + // Cr.Nic. + // rhs: cr nic = 2 ut - ut-1 + h^2c^2/2 b + // A: (h2 c2/2 dx)=s , (1+4s)uij + s ui-1j + ... + MakeRhsWE kernMakeRhs(flags, rhs, ut,utm1, s, crankNic); + + const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); + GridCgInterface *gcg; + if (flags.is3D()) + gcg = new GridCg<ApplyMatrix >(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); + else + gcg = new GridCg<ApplyMatrix2D>(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); + + gcg->setAccuracy( cgAccuracy ); + gcg->setUseResNorm( true ); + + // optional preconditioning + //gcg->setPreconditioner( precondition ? GridCgInterface::PC_mICP : GridCgInterface::PC_None, &pca0, &pca1, &pca2, &pca3); + + for (int iter=0; iter<maxIter; iter++) { + if (!gcg->iterate()) iter=maxIter; + } + debMsg("FluidSolver::solvePressure iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1); + + utm1.swap( ut ); + ut.copyFrom( out ); + + delete gcg; +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "cgSolveWE" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& ut = *_args.getPtr<Grid<Real> >("ut",1,&_lock); Grid<Real>& utm1 = *_args.getPtr<Grid<Real> >("utm1",2,&_lock); Grid<Real>& out = *_args.getPtr<Grid<Real> >("out",3,&_lock); bool crankNic = _args.getOpt<bool >("crankNic",4,false,&_lock); Real cSqr = _args.getOpt<Real >("cSqr",5,0.25,&_lock); Real cgMaxIterFac = _args.getOpt<Real >("cgMaxIterFac",6,1.5,&_lock); Real cgAccuracy = _args.getOpt<Real >("cgAccuracy",7,1e-5 ,&_lock); _retval = getPyNone(); cgSolveWE(flags,ut,utm1,out,crankNic,cSqr,cgMaxIterFac,cgAccuracy); _args.check(); } pbFinalizePlugin(parent,"cgSolveWE" ); return _retval; } catch(std::exception& e) { pbSetError("cgSolveWE",e.what()); return 0; } } static const Pb::Register _RP_cgSolveWE ("","cgSolveWE",_W_3); + + + + +} //namespace + + + diff --git a/source/blender/python/manta_pp/pwrapper/manta.h b/source/blender/python/manta_pp/pwrapper/manta.h new file mode 100644 index 00000000000..87e5c95252d --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/manta.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Main + * + ******************************************************************************/ + +#ifndef _MANTA_H +#define _MANTA_H + +// Remove preprocessor keywords, so there won't infere with autocompletion etc. +#define KERNEL +#define PYTHON + +// Define plugin documentation group +// all plugin functions and classes will automatically be added to this group +//! @defgroup Plugins Plugins +//! @defgroup PyClasses Classes exposed to Python + +#include "general.h" +#include "vectorbase.h" +#include "registry.h" +#include "pclass.h" +#include "pconvert.h" +#include "fluidsolver.h" + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/pwrapper/manta_api.cpp b/source/blender/python/manta_pp/pwrapper/manta_api.cpp new file mode 100644 index 00000000000..4184a32ec6f --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/manta_api.cpp @@ -0,0 +1,27 @@ +#include "Python.h" +//#include "manta_api.h" +#include "manta.h" +#include "../general.h" + +using namespace std; +using namespace Manta; + +#if PY_MAJOR_VERSION >= 3 +typedef wchar_t pyChar; +typedef wstring pyString; +#else +typedef char pyChar; +typedef string pyString; +#endif + +//#ifdef __cplusplus +extern "C" { +//#endif +PyObject * PyInit_Manta(void) +{ + return Pb::PyInit_Main_Obj(); +} + +//#ifdef __cplusplus +} +//#endif diff --git a/source/blender/python/manta_pp/pwrapper/manta_api.h b/source/blender/python/manta_pp/pwrapper/manta_api.h new file mode 100644 index 00000000000..3fbfe6f522c --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/manta_api.h @@ -0,0 +1,6 @@ +#ifndef _MANTA_API_H_ +#define _MANTA_API_H_ +//#include "pythonInclude.h" +PyObject * PyInit_Manta(void); +PyMODINIT_FUNC PyInit_Main_Link(void); +#endif diff --git a/source/blender/python/manta_pp/pwrapper/pclass.cpp b/source/blender/python/manta_pp/pwrapper/pclass.cpp new file mode 100644 index 00000000000..838ae3f3b84 --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pclass.cpp @@ -0,0 +1,177 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Functions for property setting/getting via python + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include "structmember.h" +#include "manta.h" +#include "general.h" +#include "timing.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// Free functions + +#ifdef GUI + extern void updateQtGui(bool full, int frame, const std::string& curPlugin); +#else + inline void updateQtGui(bool full, int frame, const std::string& curPlugin) {} +#endif + +void pbPreparePlugin(FluidSolver* parent, const string& name) { + TimingData::instance().start(parent, name); +} + +void pbFinalizePlugin(FluidSolver *parent, const string& name) { + TimingData::instance().stop(parent, name); + + // GUI update, also print name of parent if there's more than one + std::ostringstream msg; + if (name != "FluidSolver::step") + { + if(parent && (parent->getNumInstances()>0) ) msg << parent->getName() << string("."); + msg << name; + } + updateQtGui(false, 0, msg.str() ); + + debMsg(name<<" done", 2); + // name unnamed PbClass Objects from var name + PbClass::renameObjects(); +} + +void pbSetError(const string& fn, const string& ex) { + cout << "Error in " << fn << endl; + if (!ex.empty()) + PyErr_SetString(PyExc_RuntimeError, ex.c_str()); +} + +//****************************************************************************** +// Helpers + +string PbTypeVec::str() const { + if (T.empty()) return ""; + string s="<"; + for (int i=0; i<(int)T.size(); i++) { + s += T[i].str(); + s += (i!=(int)T.size()-1) ? ',' : '>'; + } + return s; +} +string PbType::str() const { + if (S=="float") return "Real"; + if (S=="manta.vec3") return "Vec3"; + return S; +} + +//****************************************************************************** +// PbClass + +vector<PbClass*> PbClass::mInstances; + +PbClass::PbClass(FluidSolver* parent, const string& name, PyObject* obj) + : mMutex(), mParent(parent), mPyObject(obj), mName(name), mHidden(false) +{ +} + +PbClass::PbClass(const PbClass& a) : mMutex(), mParent(a.mParent), mPyObject(0), mName("_unnamed"), mHidden(false) +{ +} + + +PbClass::~PbClass() { + for(vector<PbClass*>::iterator it = mInstances.begin(); it != mInstances.end(); ++it) { + if (*it == this) { + mInstances.erase(it); + break; + } + } +} + +void PbClass::lock() { + mMutex.lock(); +} +void PbClass::unlock() { + mMutex.unlock(); +} +bool PbClass::tryLock() { + return mMutex.tryLock(); +} + +PbClass* PbClass::getInstance(int idx) { + if (idx<0 || idx > (int)mInstances.size()) + errMsg("PbClass::getInstance(): invalid index"); + return mInstances[idx]; +} + +int PbClass::getNumInstances() { + return mInstances.size(); +} + +bool PbClass::isNullRef(PyObject* obj) { + return PyLong_Check(obj) && PyLong_AsDouble(obj)==0; +} + +void PbClass::registerObject(PyObject* obj, PbArgs* args) { + // cross link + Pb::setReference(this, obj); + mPyObject = obj; + + mInstances.push_back(this); + + if (args) { + string _name = args->getOpt<std::string>("name",-1,""); + if (!_name.empty()) setName(_name); + } +} + +PbClass* PbClass::createPyObject(const string& classname, const string& name, PbArgs& args, PbClass* parent) { + return Pb::createPy(classname,name,args,parent); +} + +void PbClass::checkParent() { + if (getParent() == NULL) { + errMsg("New class " + mName + ": no parent given -- specify using parent=xxx !"); + } +} +//! Assign unnamed PbClass objects their Python variable name +void PbClass::renameObjects() { + PyObject* sys_mod_dict = PyImport_GetModuleDict(); + PyObject* loc_mod = PyMapping_GetItemString(sys_mod_dict, (char*)"__main__"); + if (!loc_mod) return; + PyObject* locdict = PyObject_GetAttrString(loc_mod, "__dict__"); + if (!locdict) return; + + // iterate all PbClass instances + for (size_t i=0; i<mInstances.size(); i++) { + PbClass* obj = mInstances[i]; + if (obj->getName().empty()) { + // empty, try to find instance in module local dictionary + + PyObject *lkey, *lvalue; + Py_ssize_t lpos = 0; + while (PyDict_Next(locdict, &lpos, &lkey, &lvalue)) { + if (lvalue == obj->mPyObject) { + string varName = fromPy<string>(PyObject_Str(lkey)); + obj->setName(varName); + //cout << "assigning variable name '" << varName << "' to unnamed instance" << endl; + break; + } + } + } + } + Py_DECREF(locdict); + Py_DECREF(loc_mod); +} + +} // namespace diff --git a/source/blender/python/manta_pp/pwrapper/pclass.h b/source/blender/python/manta_pp/pwrapper/pclass.h new file mode 100644 index 00000000000..418d0d8dfaf --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pclass.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for all Python-exposed classes + * + ******************************************************************************/ + +// ----------------------------------------------------------------- +// NOTE: +// Do not include this file in user code, include "manta.h" instead +// ----------------------------------------------------------------- + +#ifdef _MANTA_H +#ifndef _PTYPE_H +#define _PTYPE_H + +#include <string> +#include <vector> +#include <map> + +#ifdef GUI +# include <QMutex> +#else +struct QMutex { + void lock() {}; + void unlock() {}; + bool tryLock() {return true;}; +}; +#endif + +namespace Manta { +struct PbClassData; +class FluidSolver; +class PbArgs; + +struct PbType { + std::string S; + std::string str() const; +}; +struct PbTypeVec { + std::vector<PbType> T; + std::string str() const; +}; + +//! Base class for all classes exposed to Python +class PbClass { +public: + PbClass(FluidSolver* parent, const std::string& name="", PyObject* obj=NULL); + PbClass(const PbClass& a); + virtual ~PbClass(); + + // basic property setter/getters + void setName(const std::string& name) { mName = name; } + std::string getName() const { return mName; } + PyObject* getPyObject() const { return mPyObject; } + void registerObject(PyObject* obj, PbArgs* args); + FluidSolver* getParent() const { return mParent; } + void setParent(FluidSolver* v) { mParent = v; } + void checkParent(); + + // hidden flag for GUI, debug output + inline bool isHidden() { return mHidden; } + inline void setHidden(bool v) { mHidden = v; } + + void lock(); + void unlock(); + bool tryLock(); + + // PbClass instance registry + static int getNumInstances(); + static PbClass* getInstance(int index); + static void renameObjects(); + + // converters + static bool isNullRef(PyObject* o); + static PbClass* createPyObject(const std::string& classname, const std::string& name, PbArgs& args, PbClass *parent); + inline bool canConvertTo(const std::string& classname) { return Pb::canConvert(mPyObject, classname); } + +protected: + QMutex mMutex; + FluidSolver* mParent; + PyObject* mPyObject; + std::string mName; + bool mHidden; + + static std::vector<PbClass*> mInstances; +}; + +//!\cond Register + +void pbFinalizePlugin(FluidSolver* parent, const std::string& name); +void pbPreparePlugin(FluidSolver* parent, const std::string& name); +void pbSetError(const std::string& fn, const std::string& ex); + +//!\endcond + +} // namespace + +#endif +#endif diff --git a/source/blender/python/manta_pp/pwrapper/pconvert.cpp b/source/blender/python/manta_pp/pwrapper/pconvert.cpp new file mode 100644 index 00000000000..916ffa7a98f --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pconvert.cpp @@ -0,0 +1,410 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Python argument wrappers and conversion tools + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include <sstream> +#include <algorithm> +#include "vectorbase.h" +#include "manta.h" + +using namespace std; + +//****************************************************************************** +// Explicit definition and instantiation of python object converters + +namespace Manta { + +extern PyTypeObject PbVec3Type; +struct PbVec3 { + PyObject_HEAD + float data[3]; +}; + +PyObject* getPyNone() { + Py_INCREF(Py_None); + return Py_None; +} +PyObject* incref(PyObject* obj) { + Py_INCREF(obj); + return obj; +} + + +/*template<> PyObject* toPy<PyObject*>(PyObject* obj) { + return obj; +}*/ +template<> PyObject* toPy<int>( const int& v) { + return PyLong_FromLong(v); +} +/*template<> PyObject* toPy<char*>(const (char*) & val) { + return PyUnicode_DecodeLatin1(val,strlen(val),"replace"); +}*/ +template<> PyObject* toPy<string>( const string& val) { + return PyUnicode_DecodeLatin1(val.c_str(),val.length(),"replace"); +} +template<> PyObject* toPy<float>( const float& v) { + return PyFloat_FromDouble(v); +} +template<> PyObject* toPy<double>( const double& v) { + return PyFloat_FromDouble(v); +} +template<> PyObject* toPy<bool>( const bool& v) { + return PyBool_FromLong(v); +} +template<> PyObject* toPy<Vec3i>(const Vec3i& v) { + float x=(float)v.x, y=(float)v.y, z=(float)v.z; + return PyObject_CallFunction((PyObject*)&PbVec3Type, (char*)"fff", x, y, z); +} +template<> PyObject* toPy<Vec3>(const Vec3& v) { + float x=(float)v.x, y=(float)v.y, z=(float)v.z; + return PyObject_CallFunction((PyObject*)&PbVec3Type, (char*)"fff", x, y, z); +} +template<> PyObject* toPy<PbClass*>(const PbClass_Ptr& obj) { + return obj->getPyObject(); +} + +template<> float fromPy<float>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + errMsg("argument is not a float"); +} +template<> double fromPy<double>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + errMsg("argument is not a double"); +} +template<> PyObject* fromPy<PyObject*>(PyObject *obj) { + return obj; +} +template<> int fromPy<int>(PyObject *obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return PyInt_AsLong(obj); +#endif + if (PyLong_Check(obj)) return PyLong_AsDouble(obj); + if (PyFloat_Check(obj)) { + double a = PyFloat_AsDouble(obj); + if (fabs(a-floor(a+0.5)) > 1e-5) + errMsg("argument is not an int"); + return (int) (a+0.5); + } + errMsg("argument is not an int"); +} +template<> string fromPy<string>(PyObject *obj) { + if (PyUnicode_Check(obj)) + return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); +#if PY_MAJOR_VERSION <= 2 + else if (PyString_Check(obj)) + return PyString_AsString(obj); +#endif + else + errMsg("argument is not a string"); +} +template<> const char* fromPy<const char*>(PyObject *obj) { + if (PyUnicode_Check(obj)) + return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); +#if PY_MAJOR_VERSION <= 2 + else if (PyString_Check(obj)) + return PyString_AsString(obj); +#endif + else errMsg("argument is not a string"); +} +template<> bool fromPy<bool>(PyObject *obj) { + if (!PyBool_Check(obj)) errMsg("argument is not a boolean"); + return PyLong_AsLong(obj) != 0; +} +template<> Vec3 fromPy<Vec3>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) { + return Vec3(((PbVec3*)obj)->data); + } + else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return Vec3(fromPy<Real>(PyTuple_GetItem(obj,0)), + fromPy<Real>(PyTuple_GetItem(obj,1)), + fromPy<Real>(PyTuple_GetItem(obj,2))); + } + errMsg("argument is not a Vec3"); +} +template<> Vec3i fromPy<Vec3i>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) { + return toVec3iChecked(((PbVec3*)obj)->data); + } + else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return Vec3i(fromPy<int>(PyTuple_GetItem(obj,0)), + fromPy<int>(PyTuple_GetItem(obj,1)), + fromPy<int>(PyTuple_GetItem(obj,2))); + } + errMsg("argument is not a Vec3i"); +} +template<> PbType fromPy<PbType>(PyObject* obj) { + PbType pb = {""}; + if (!PyType_Check(obj)) + return pb; + + const char* tname = ((PyTypeObject*)obj)->tp_name; + pb.S = tname; + return pb; +} +template<> PbTypeVec fromPy<PbTypeVec>(PyObject* obj) { + PbTypeVec vec; + if (PyType_Check(obj)) { + vec.T.push_back(fromPy<PbType>(obj)); + } else if (PyTuple_Check(obj)) { + int sz = PyTuple_Size(obj); + for (int i=0; i< sz; i++) + vec.T.push_back(fromPy<PbType>(PyTuple_GetItem(obj,i))); + } + else + errMsg("argument is not a type tuple"); + return vec; +} + +template<class T> T* tmpAlloc(PyObject* obj,std::vector<void*>* tmp) { + if (!tmp) throw Error("dynamic de-ref not supported for this type"); + void* ptr = malloc(sizeof(T)); + tmp->push_back(ptr); + + *((T*)ptr) = fromPy<T>(obj); + return (T*)ptr; +} +template<> float* fromPyPtr<float>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<float>(obj,tmp); } +template<> double* fromPyPtr<double>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<double>(obj,tmp); } +template<> int* fromPyPtr<int>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<int>(obj,tmp); } +template<> std::string* fromPyPtr<std::string>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<std::string>(obj,tmp); } +template<> bool* fromPyPtr<bool>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<bool>(obj,tmp); } +template<> Vec3* fromPyPtr<Vec3>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<Vec3>(obj,tmp); } +template<> Vec3i* fromPyPtr<Vec3i>(PyObject* obj, std::vector<void*>* tmp) { return tmpAlloc<Vec3i>(obj,tmp); } + +template<> bool isPy<float>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + return PyFloat_Check(obj) || PyLong_Check(obj); +} +template<> bool isPy<double>(PyObject* obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + return PyFloat_Check(obj) || PyLong_Check(obj); +} +template<> bool isPy<PyObject*>(PyObject *obj) { + return true; +} +template<> bool isPy<int>(PyObject *obj) { +#if PY_MAJOR_VERSION <= 2 + if (PyInt_Check(obj)) return true; +#endif + if (PyLong_Check(obj)) return true; + if (PyFloat_Check(obj)) { + double a = PyFloat_AsDouble(obj); + return fabs(a-floor(a+0.5)) < 1e-5; + } + return false; +} +template<> bool isPy<string>(PyObject *obj) { + if (PyUnicode_Check(obj)) return true; +#if PY_MAJOR_VERSION <= 2 + if (PyString_Check(obj)) return true; +#endif + return false; +} +template<> bool isPy<const char*>(PyObject *obj) { + if (PyUnicode_Check(obj)) return true; +#if PY_MAJOR_VERSION <= 2 + if (PyString_Check(obj)) return true; +#endif + return false; +} +template<> bool isPy<bool>(PyObject *obj) { + return PyBool_Check(obj); +} +template<> bool isPy<Vec3>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) return true; + if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return isPy<Real>(PyTuple_GetItem(obj,0)) && + isPy<Real>(PyTuple_GetItem(obj,1)) && + isPy<Real>(PyTuple_GetItem(obj,2)); + } + return false; +} +template<> bool isPy<Vec3i>(PyObject* obj) { + if (PyObject_IsInstance(obj, (PyObject*)&PbVec3Type)) return true; + if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { + return isPy<int>(PyTuple_GetItem(obj,0)) && + isPy<int>(PyTuple_GetItem(obj,1)) && + isPy<int>(PyTuple_GetItem(obj,2)); + } + return false; +} +template<> bool isPy<PbType>(PyObject* obj) { + return PyType_Check(obj); +} + +//****************************************************************************** +// PbArgs class defs + +PbArgs PbArgs::EMPTY(NULL,NULL); + +PbArgs::PbArgs(PyObject* linarg, PyObject* dict) : mLinArgs(0), mKwds(0) { + setup(linarg, dict); +} +PbArgs::~PbArgs() { + for(int i=0; i<(int)mTmpStorage.size(); i++) + free(mTmpStorage[i]); + mTmpStorage.clear(); +} + +void PbArgs::copy(PbArgs& a) { + mKwds = a.mKwds; + mData = a.mData; + mLinData = a.mLinData; + mLinArgs = a.mLinArgs; + +} +void PbArgs::clear() { + mLinArgs = 0; + mKwds = 0; + mData.clear(); + mLinData.clear(); +} + +PbArgs& PbArgs::operator=(const PbArgs& a) { +// mLinArgs = 0; +// mKwds = 0; + return *this; +} + +void PbArgs::setup(PyObject* linarg, PyObject* dict) { + if (dict) { + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + DataElement el; + el.obj = value; + el.visited = false; + mData[fromPy<string>(key)] = el; + } + mKwds = dict; + } + if (linarg) { + size_t len = PyTuple_Size(linarg); + for (size_t i=0; i<len; i++) { + DataElement el; + el.obj = PyTuple_GetItem(linarg, i); + el.visited = false; + mLinData.push_back(el); + } + mLinArgs = linarg; + } +} + +void PbArgs::addLinArg(PyObject* obj) { + DataElement el = { obj, false }; + mLinData.push_back(el); +} + +void PbArgs::check() { + if (has("nocheck")) return; + + for(map<string, DataElement>::iterator it = mData.begin(); it != mData.end(); it++) { + if (!it->second.visited) + errMsg("Argument '" + it->first + "' unknown"); + } + for(size_t i=0; i<mLinData.size(); i++) { + if (!mLinData[i].visited) { + stringstream s; + s << "Function does not read argument number #" << i; + errMsg(s.str()); + } + } +} + +FluidSolver* PbArgs::obtainParent() { + FluidSolver* solver = getPtrOpt<FluidSolver>("solver",-1,NULL); + if (solver != 0) return solver; + + for(map<string, DataElement>::iterator it = mData.begin(); it != mData.end(); it++) { + PbClass* obj = Pb::objFromPy(it->second.obj); + + if (obj) { + if (solver == NULL) + solver = obj->getParent(); + } + } + for(vector<DataElement>::iterator it = mLinData.begin(); it != mLinData.end(); it++) { + PbClass* obj = Pb::objFromPy(it->obj); + + if (obj) { + if (solver == NULL) + solver = obj->getParent(); + } + } + + return solver; +} + +void PbArgs::visit(int number, const string& key) { + if (number >= 0 && number < (int)mLinData.size()) + mLinData[number].visited = true; + map<string, DataElement>::iterator lu = mData.find(key); + if (lu != mData.end()) + lu->second.visited = true; +} + +PyObject* PbArgs::getItem(const std::string& key, bool strict, ArgLocker* lk) { + map<string, DataElement>::iterator lu = mData.find(key); + if (lu == mData.end()) { + if (strict) + errMsg ("Argument '" + key + "' is not defined."); + return NULL; + } + PbClass* pbo = Pb::objFromPy(lu->second.obj); + // try to lock + if (pbo && lk) lk->add(pbo); + return lu->second.obj; +} + +PyObject* PbArgs::getItem(size_t number, bool strict, ArgLocker* lk) { + if (number >= mLinData.size()) { + if (!strict) + return NULL; + stringstream s; + s << "Argument number #" << number << " not specified."; + errMsg(s.str()); + } + PbClass* pbo = Pb::objFromPy(mLinData[number].obj); + // try to lock + if (pbo && lk) lk->add(pbo); + return mLinData[number].obj; +} + +//****************************************************************************** +// ArgLocker class defs + +void ArgLocker::add(PbClass* p) { + if (find(locks.begin(), locks.end(), p) == locks.end()) { + locks.push_back(p); + p->lock(); + } +} +ArgLocker::~ArgLocker() { + for (size_t i=0; i<locks.size(); i++) + locks[i]->unlock(); + locks.clear(); +} + +} // namespace diff --git a/source/blender/python/manta_pp/pwrapper/pconvert.h b/source/blender/python/manta_pp/pwrapper/pconvert.h new file mode 100644 index 00000000000..7b8721254a5 --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pconvert.h @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Python argument wrappers and conversion tools + * + ******************************************************************************/ + +// ----------------------------------------------------------------- +// NOTE: +// Do not include this file in user code, include "manta.h" instead +// ----------------------------------------------------------------- + +#ifdef _MANTA_H +#ifndef _PCONVERT_H +#define _PCONVERT_H + +#include <string> +#include <map> +#include <vector> + +namespace Manta { +template<class T> class Grid; + + +//! Locks the given PbClass Arguments until ArgLocker goes out of scope +struct ArgLocker { + void add(PbClass* p); + ~ArgLocker(); + std::vector<PbClass*> locks; +}; + +PyObject* getPyNone(); + +// for PbClass-derived classes +template<class T> T* fromPyPtr(PyObject* obj, std::vector<void*>* tmp) { + if (PbClass::isNullRef(obj)) + return 0; + PbClass* pbo = Pb::objFromPy(obj); + const std::string& type = Namify<T>::S; + if (!pbo || !(pbo->canConvertTo(type))) + throw Error("can't convert argument to " + type+"*"); + return (T*)(pbo); +} + +template<> float* fromPyPtr<float>(PyObject* obj, std::vector<void*>* tmp); +template<> double* fromPyPtr<double>(PyObject* obj, std::vector<void*>* tmp); +template<> int* fromPyPtr<int>(PyObject* obj, std::vector<void*>* tmp); +template<> std::string* fromPyPtr<std::string>(PyObject* obj, std::vector<void*>* tmp); +template<> bool* fromPyPtr<bool>(PyObject* obj, std::vector<void*>* tmp); +template<> Vec3* fromPyPtr<Vec3>(PyObject* obj, std::vector<void*>* tmp); +template<> Vec3i* fromPyPtr<Vec3i>(PyObject* obj, std::vector<void*>* tmp); + +PyObject* incref(PyObject* obj); +template<class T> PyObject* toPy(const T& v) { + PyObject* obj = v.getPyObject(); + if (obj) { + return incref(obj); + } + T* co = new T (v); + const std::string& type = Namify<typename remove_pointers<T>::type>::S; + return Pb::copyObject(co,type); +} +template<class T> bool isPy(PyObject* obj) { + if (PbClass::isNullRef(obj)) + return false; + PbClass* pbo = Pb::objFromPy(obj); + const std::string& type = Namify<typename remove_pointers<T>::type>::S; + return pbo && pbo->canConvertTo(type); +} + +template<class T> T fromPy(PyObject* obj) { + throw Error("Unknown type conversion. Did you pass a PbClass by value? (you shouldn't)"); +} + +// builtin types +template<> float fromPy<float>(PyObject* obj); +template<> double fromPy<double>(PyObject* obj); +template<> int fromPy<int>(PyObject *obj); +template<> PyObject* fromPy<PyObject*>(PyObject *obj); +template<> std::string fromPy<std::string>(PyObject *obj); +template<> const char* fromPy<const char*>(PyObject *obj); +template<> bool fromPy<bool>(PyObject *obj); +template<> Vec3 fromPy<Vec3>(PyObject* obj); +template<> Vec3i fromPy<Vec3i>(PyObject* obj); +template<> PbType fromPy<PbType>(PyObject* obj); +template<> PbTypeVec fromPy<PbTypeVec>(PyObject* obj); + +template<> PyObject* toPy<int>( const int& v); +template<> PyObject* toPy<std::string>( const std::string& val); +template<> PyObject* toPy<float>( const float& v); +template<> PyObject* toPy<double>( const double& v); +template<> PyObject* toPy<bool>( const bool& v); +template<> PyObject* toPy<Vec3i>( const Vec3i& v); +template<> PyObject* toPy<Vec3>( const Vec3& v); +typedef PbClass* PbClass_Ptr; +template<> PyObject* toPy<PbClass*>( const PbClass_Ptr & obj); + +template<> bool isPy<float>(PyObject* obj); +template<> bool isPy<double>(PyObject* obj); +template<> bool isPy<int>(PyObject *obj); +template<> bool isPy<PyObject*>(PyObject *obj); +template<> bool isPy<std::string>(PyObject *obj); +template<> bool isPy<const char*>(PyObject *obj); +template<> bool isPy<bool>(PyObject *obj); +template<> bool isPy<Vec3>(PyObject* obj); +template<> bool isPy<Vec3i>(PyObject* obj); +template<> bool isPy<PbType>(PyObject* obj); + +//! Encapsulation of python arguments +class PbArgs { +public: + PbArgs(PyObject *linargs = NULL, PyObject* dict = NULL); + ~PbArgs(); + void setup(PyObject *linargs = NULL, PyObject* dict = NULL); + + void check(); + FluidSolver* obtainParent(); + + inline int numLinArgs() { return mLinData.size(); } + + inline bool has(const std::string& key) { + return getItem(key, false) != NULL; + } + + inline PyObject* linArgs() { return mLinArgs; } + inline PyObject* kwds() { return mKwds; } + + void addLinArg(PyObject* obj); + + template<class T> inline void add(const std::string& key, T arg) { + DataElement el = { toPy(arg), false }; + mData[key] = el; + } + template<class T> inline T get(const std::string& key, int number=-1, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPy<T>(o); + o = getItem(number, false, lk); + if (o) return fromPy<T>(o); + errMsg ("Argument '" + key + "' is not defined."); + } + template<class T> inline T getOpt(const std::string& key, int number, T defarg, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPy<T>(o); + if (number >= 0) o = getItem(number, false, lk); + return (o) ? fromPy<T>(o) : defarg; + } + template<class T> inline T* getPtrOpt(const std::string& key, int number, T* defarg, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPyPtr<T>(o,&mTmpStorage); + if (number >= 0) o = getItem(number, false, lk); + return o ? fromPyPtr<T>(o,&mTmpStorage) : defarg; + } + template<class T> inline T* getPtr(const std::string& key, int number = -1, ArgLocker *lk=NULL) { + visit(number, key); + PyObject* o = getItem(key, false, lk); + if (o) return fromPyPtr<T>(o,&mTmpStorage); + o = getItem(number, false, lk); + if(o) return fromPyPtr<T>(o,&mTmpStorage); + errMsg ("Argument '" + key + "' is not defined."); + } + + + // automatic template type deduction + template<class T> bool typeCheck(int num, const std::string& name) { + PyObject* o = getItem(name, false, 0); + if (!o) + o = getItem(num, false, 0); + return o ? isPy<typename remove_pointers<T>::type>(o) : false; + } + + PbArgs& operator=(const PbArgs& a); // dummy + void copy(PbArgs& a); + void clear(); + void visit(int num, const std::string& key); + + static PbArgs EMPTY; + +protected: + PyObject* getItem(const std::string& key, bool strict, ArgLocker* lk = NULL); + PyObject* getItem(size_t number, bool strict, ArgLocker* lk = NULL); + + struct DataElement { + PyObject *obj; + bool visited; + }; + std::map<std::string, DataElement> mData; + std::vector<DataElement> mLinData; + PyObject* mLinArgs, *mKwds; + std::vector<void*> mTmpStorage; +}; + + +} // namespace +#endif +#endif diff --git a/source/blender/python/manta_pp/pwrapper/pvec3.cpp b/source/blender/python/manta_pp/pwrapper/pvec3.cpp new file mode 100644 index 00000000000..0743fe6914b --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pvec3.cpp @@ -0,0 +1,286 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vec3 class extension for python + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include <math.h> +#include <string> +#include <sstream> +#include <limits> +#include "vectorbase.h" +#include "structmember.h" +#include "manta.h" + +using namespace std; + +namespace Manta { + +extern PyTypeObject PbVec3Type; + +struct PbVec3 { + PyObject_HEAD + float data[3]; +}; + +static void PbVec3Dealloc(PbVec3* self) { + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * PbVec3New(PyTypeObject *type, PyObject *args, PyObject *kwds) { + return type->tp_alloc(type, 0); +} + +static int PbVec3Init(PbVec3 *self, PyObject *args, PyObject *kwds) { + + float x1 = numeric_limits<float>::quiet_NaN(), x2=x1, x3=x1; + if (!PyArg_ParseTuple(args,"|fff",&x1, &x2, &x3)) + return -1; + + if (!c_isnan(x1)) { + self->data[0] = x1; + if (!c_isnan(x2) && !c_isnan(x3)) { + self->data[1] = x2; + self->data[2] = x3; + } else { + self->data[1] = x1; + self->data[2] = x1; + } + } else { + self->data[0] = 0; + self->data[1] = 0; + self->data[2] = 0; + } + return 0; +} + +static PyObject* PbVec3Repr(PbVec3* self) { + Manta::Vec3 v(self->data[0], self->data[1], self->data[2]); + return PyUnicode_FromFormat(v.toString().c_str()); +} + +static PyMemberDef PbVec3Members[] = { + {(char*)"x", T_FLOAT, offsetof(PbVec3, data), 0, (char*)"X"}, + {(char*)"y", T_FLOAT, offsetof(PbVec3, data)+sizeof(float), 0, (char*)"Y"}, + {(char*)"z", T_FLOAT, offsetof(PbVec3, data)+sizeof(float)*2, 0, (char*)"Z"}, + {NULL} // Sentinel +}; + +static PyMethodDef PbVec3Methods[] = { + //{"name", (PyCFunction)Noddy_name, METH_NOARGS, "Return the name, combining the first and last name" }, + {NULL} // Sentinel +}; + +// operator overloads + +inline PyObject* PbNew(const Vec3& a) { + PbVec3* obj = (PbVec3*)PbVec3New(&PbVec3Type,0,0); + obj->data[0] = a.x; + obj->data[1] = a.y; + obj->data[2] = a.z; + return (PyObject*)obj; +} + +#define CONVERTVEC(obj) \ + Vec3 v##obj; \ + if (PyObject_TypeCheck(obj, &PbVec3Type)) \ + v##obj = Vec3(&( ((PbVec3*)obj)->data[0])); \ + else if (PyFloat_Check(obj)) \ + v##obj = Vec3(PyFloat_AsDouble(obj)); \ + else if (PyLong_Check(obj)) \ + v##obj = Vec3(PyLong_AsDouble(obj)); \ + else { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + +#define OPHEADER \ + if (!PyObject_TypeCheck(a, &PbVec3Type) && !PyObject_TypeCheck(b, &PbVec3Type)) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + CONVERTVEC(a) \ + CONVERTVEC(b) + +#define OPHEADER1 \ + if (!PyObject_TypeCheck(a, &PbVec3Type)) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + CONVERTVEC(a) + +PyObject* PbVec3Add(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va+vb); +} + +PyObject* PbVec3Sub(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va-vb); +} + +PyObject* PbVec3Mult(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va*vb); +} + +PyObject* PbVec3Div(PyObject* a, PyObject* b) { + OPHEADER + return PbNew(va/vb); +} + +PyObject* PbVec3Negative(PyObject* a) { + OPHEADER1 + return PbNew(-va); +} + +// numbers are defined subtely different in Py3 (WTF?) +#if PY_MAJOR_VERSION >= 3 +static PyNumberMethods PbVec3NumberMethods = { + (binaryfunc)PbVec3Add, // binaryfunc nb_add; + (binaryfunc)PbVec3Sub, // binaryfunc nb_sub; + (binaryfunc)PbVec3Mult, // binaryfunc nb_mult; + 0, // binaryfunc nb_remainder; + 0, // binaryfunc nb_divmod; + 0, // ternaryfunc nb_power; + (unaryfunc)PbVec3Negative, // unaryfunc nb_negative; + 0, // unaryfunc nb_positive; + 0, // unaryfunc nb_absolute; + 0, // inquiry nb_bool; + 0, // unaryfunc nb_invert; + 0, // binaryfunc nb_lshift; + 0, // binaryfunc nb_rshift; + 0, // binaryfunc nb_and; + 0, // binaryfunc nb_xor; + 0, // binaryfunc nb_or; + 0, // unaryfunc nb_int; + 0, // void *nb_reserved; + 0, // unaryfunc nb_float; + 0, // binaryfunc nb_inplace_add; + 0, // binaryfunc nb_inplace_subtract; + 0, // binaryfunc nb_inplace_multiply; + 0, // binaryfunc nb_inplace_remainder; + 0, // ternaryfunc nb_inplace_power; + 0, // binaryfunc nb_inplace_lshift; + 0, // binaryfunc nb_inplace_rshift; + 0, // binaryfunc nb_inplace_and; + 0, // binaryfunc nb_inplace_xor; + 0, // binaryfunc nb_inplace_or; + + 0, // binaryfunc nb_floor_divide; + (binaryfunc)PbVec3Div, // binaryfunc nb_true_divide; + 0, // binaryfunc nb_inplace_floor_divide; + 0, // binaryfunc nb_inplace_true_divide; + + 0 // unaryfunc nb_index; +}; +#else +static PyNumberMethods PbVec3NumberMethods = { + (binaryfunc)PbVec3Add, // binaryfunc nb_add; + (binaryfunc)PbVec3Sub, // binaryfunc nb_sub; + (binaryfunc)PbVec3Mult, // binaryfunc nb_mult; + 0, // binaryfunc nb_divide; + 0, // binaryfunc nb_remainder; + 0, // binaryfunc nb_divmod; + 0, // ternaryfunc nb_power; + (unaryfunc)PbVec3Negative, // unaryfunc nb_negative; + 0, // unaryfunc nb_positive; + 0, // unaryfunc nb_absolute; + 0, // inquiry nb_nonzero; + 0, // unaryfunc nb_invert; + 0, // binaryfunc nb_lshift; + 0, // binaryfunc nb_rshift; + 0, // binaryfunc nb_and; + 0, // binaryfunc nb_xor; + 0, // binaryfunc nb_or; + 0, // coercion nb_coerce; + 0, // unaryfunc nb_int; + 0, // unaryfunc nb_long; + 0, // unaryfunc nb_float; + 0, // unaryfunc nb_oct; + 0, // unaryfunc nb_hex; + 0, // binaryfunc nb_inplace_add; + 0, // binaryfunc nb_inplace_subtract; + 0, // binaryfunc nb_inplace_multiply; + 0, // binaryfunc nb_inplace_divide; + 0, // binaryfunc nb_inplace_remainder; + 0, // ternaryfunc nb_inplace_power; + 0, // binaryfunc nb_inplace_lshift; + 0, // binaryfunc nb_inplace_rshift; + 0, // binaryfunc nb_inplace_and; + 0, // binaryfunc nb_inplace_xor; + 0, // binaryfunc nb_inplace_or; + 0, // binaryfunc nb_floor_divide; + (binaryfunc)PbVec3Div, // binaryfunc nb_true_divide; + 0, // binaryfunc nb_inplace_floor_divide; + 0, // binaryfunc nb_inplace_true_divide; + 0, // unaryfunc nb_index; +}; +#endif + +PyTypeObject PbVec3Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "manta.vec3", /* tp_name */ + sizeof(PbVec3), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PbVec3Dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)PbVec3Repr, /* tp_repr */ + &PbVec3NumberMethods, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + "float vector type", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PbVec3Methods, /* tp_methods */ + PbVec3Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PbVec3Init, /* tp_init */ + 0, /* tp_alloc */ + PbVec3New, /* tp_new */ +}; + +inline PyObject* castPy(PyTypeObject* p) { + return reinterpret_cast<PyObject*>(static_cast<void*>(p)); +} + +void PbVecInitialize(PyObject* module) { + if (PyType_Ready(&PbVec3Type) < 0) errMsg("can't initialize Vec3 type"); + + Py_INCREF(castPy(&PbVec3Type)); + PyModule_AddObject(module, "vec3", (PyObject *)&PbVec3Type); +} +const static Pb::Register _REG(PbVecInitialize); + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_pp/pwrapper/pymain.cpp b/source/blender/python/manta_pp/pwrapper/pymain.cpp new file mode 100644 index 00000000000..83bc58738e6 --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pymain.cpp @@ -0,0 +1,264 @@ +/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2011 Tobias Pfaff, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * GNU General Public License (GPL)
+ * http://www.gnu.org/licenses
+ *
+ * Main file
+ *
+ ******************************************************************************/
+#ifndef _MANTA_PYMAIN_CPP_
+#define _MANTA_PYMAIN_CPP_
+
+#include "pythonInclude.h"
+#include <stdio.h>
+#include "manta.h"
+#include "../general.h"
+#include "grid.h"
+#include "fileio.h"
+#include "wchar.h"
+#include <fstream>
+using namespace std;
+namespace Manta {
+ extern void guiMain(int argc, char* argv[]);
+ extern void guiWaitFinish();
+}
+
+using namespace std;
+using namespace Manta;
+
+#if PY_MAJOR_VERSION >= 3
+typedef wchar_t pyChar;
+typedef wstring pyString;
+#else
+typedef char pyChar;
+typedef string pyString;
+#endif
+
+//*****************************************************************************
+// main...
+static bool manta_initialized = false;
+//def del_var(x):\n\
+// if globals().has_key(x):\n\
+// del x \n\
+// except NameError as e:\n\
+print (\'not deleted\') \n\
+
+const string clean_code1 = "\n\
+if 's' in globals() : del s \n\
+if 'uvs' in globals() : del uvs \n\
+if 'velInflow' in globals() : del velInflow \n\
+if 'res' in globals() : del res \n\
+if 'gs' in globals() : del gs \n\
+if 'noise' in globals() : del noise \n\
+if 'source' in globals() : del source \n\
+if 'sourceVel' in globals() : del sourceVel \n\
+if 'flags' in globals() : del flags \n\
+if 'vel' in globals() : del vel \n\
+if 'density' in globals() : del density \n\
+if 'pressure' in globals() : del pressure \n\
+if 'energy' in globals() : del energy \n\
+if 'tempFlag' in globals() : del tempFlag \n\
+if 'sdf_flow' in globals() : del sdf_flow \n\
+if 'source_shape' in globals() : del source_shape\n";
+const static string clean_code2 = "del s;del noise;del xl;del xl_noise;del xl_wltnoise;";
+ //for latter full object release
+ //const static string clean_code2 = "del [s, noise, source, sourceVel, xl, xl_vel, xl_density, xl_flags,xl_source, xl_noise, flags, vel, density, pressure, energy, tempFlag, sdf_flow, forces, source,source_shape, xl_wltnoise]";
+
+void export_fields(int size_x, int size_y, int size_z, float *f_x, float*f_y, float*f_z, char *filename)
+{
+ assert(size_x>0 && size_y>0 && size_z>0);
+ assert(f_x != NULL);
+ assert(f_y != NULL);
+ assert(f_z != NULL);
+ FluidSolver dummy(Vec3i(size_x,size_y,size_z));
+ Grid<Vec3> force_fields(&dummy, false);
+ for (int x=0; x < size_x; ++x)
+ {
+ for (int y=0; y < size_y; ++y)
+ {
+ for (int z=0; z < size_z; ++z)
+ {
+ force_fields.get(x, y, z) = Vec3(f_x[x],f_y[y],f_z[z]);
+ }
+ }
+ }
+ writeGridUni(filename, &force_fields);
+ /*rename after export successful*/
+
+// writeGridTxt("s.txt", &force_fields);
+}
+
+void export_em_fields(float *em_map, float flow_density, int min_x, int min_y, int min_z, int max_x, int max_y, int max_z, int d_x, int d_y, int d_z, float *inf, float *vel)
+{
+// assert(size_x>0 && size_y>0 && size_z>0);
+ assert(inf != NULL);
+// assert(vel != NULL);
+
+ FluidSolver dummy(Vec3i(d_x,d_y,d_z));
+ Grid<Real> em_inf_fields(&dummy, false);
+ em_inf_fields.clear();
+ const char* influence_name = "manta_em_influence.uni";
+// const char* velocity_name = "em_vel_fields.uni";
+ ifstream em_file(influence_name);
+ if (em_file.good()) {
+// em_inf_fields.load(influence_name);
+ }
+ em_file.close();
+
+// Grid<Vec3> em_vel_fields(&dummy, false);
+// ifstream vel_file(velocity_name);
+// if (vel_file.good()) {
+// em_vel_fields.load(velocity_name);
+// }
+// vel_file.close();
+ int index(0);
+ Vec3i em_size(max_x - min_x, max_y - min_y, max_z - min_z);
+ int em_size_x =em_size[0];
+ int em_size_xy = em_size[0] * em_size[1];
+ for (int x=0; x < em_size[0]; ++x)
+ {
+ for (int y=0; y < em_size[1]; ++y)
+ {
+ for (int z=0; z < em_size[2]; ++z)
+ {
+ index = x + y * em_size_x + z * em_size_xy;
+ em_inf_fields.get(x + min_x, y + min_y, z + min_z) += flow_density * inf[index];//f_x[x],f_y[y],f_z[z]);
+// if(vel != NULL)
+// em_vel_fields.get(x, y, z) = Vec3(vel[index*3],vel[index*3+1],vel[index*3+2]);
+ }
+ }
+ }
+ /*testing grid passing to Python directly*/
+// PyGILState_STATE gilstate = PyGILState_Ensure();
+// PyObject *main = PyImport_AddModule("__main__"); // borrowed
+// if (main == NULL){
+// printf("ERROR: No Main python Module");
+// return;
+// }
+// PyObject *globals = PyModule_GetDict(main); // borrowed
+// PyObject *value = 0;
+// em_inf_fields.registerObject(value, NULL);
+// PyObject *test = 0;
+// test = em_inf_fields.getPyObject();
+// if (value == NULL){
+// printf("ERROR: can't retrieve value");
+// return;
+// }
+// if (PyDict_SetItemString(globals, "source_grid", value) < 0){
+// printf("ERROR: Can't set value");
+// return;
+// }
+// Py_DECREF(value);
+// PyGILState_Release(gilstate);
+ writeGridUni("manta_em_influence.uni", &em_inf_fields);
+ writeGridTxt("manta_em_influence.txt", &em_inf_fields);
+// if (vel != NULL){
+// writeGridUni("em_vel_fields.uni", &em_vel_fields);
+// writeGridTxt("em_vel_fields.txt", &em_vel_fields);
+// }
+}
+
+void export_force_fields(int size_x, int size_y, int size_z, float *f_x, float*f_y, float*f_z)
+{
+ export_fields(size_x, size_y, size_z, f_x, f_y, f_z, "manta_forces.uni");
+}
+
+void runMantaScript(const string& ss,vector<string>& args) {
+ string filename = args[0];
+
+ // Initialize extension classes and wrappers
+ srand(0);
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ /*cleaning possible previous setups*/
+ PyRun_SimpleString(clean_code1.c_str());
+
+ if (! manta_initialized)
+ {
+ debMsg("running manta init?", 0);
+ Pb::setup(filename, args);
+ manta_initialized = true;
+ }
+ // Pass through the command line arguments
+ // for Py3k compatability, convert to wstring
+ vector<pyString> pyArgs(args.size());
+ const pyChar ** cargs = new const pyChar* [args.size()];
+ for (size_t i=0; i<args.size(); i++) {
+ pyArgs[i] = pyString(args[i].begin(), args[i].end());
+ cargs[i] = pyArgs[i].c_str();
+ }
+ PySys_SetArgv( args.size(), (pyChar**) cargs);
+
+ // Try to load python script
+// FILE* fp = fopen(filename.c_str(),"rb");
+// if (fp == NULL) {
+// debMsg("Cannot open '" << filename << "'", 0);
+// Pb::finalize();
+// return;
+// }
+
+ // Run the python script file
+ debMsg("Loading script '" << filename << "'", 0);
+#if defined(WIN32) || defined(_WIN32)
+ // known bug workaround: use simplestring
+ fseek(fp,0,SEEK_END);
+ long filelen=ftell(fp);
+ fseek(fp,0,SEEK_SET);
+ char* buf = new char[filelen+1];
+ fread(buf,filelen,1,fp);
+ buf[filelen] = '\0';
+ fclose(fp);
+ PyRun_SimpleString(buf);
+ delete[] buf;
+#else
+ // for linux, use this as it produces nicer error messages
+ string toExec = "";
+
+ PyRun_SimpleString(ss.c_str());
+// PyRun_SimpleFileEx(fp, filename.c_str(), 0);
+// for (int frame=0; frame < 4; ++frame)
+// {
+// std::string frame_str = static_cast<ostringstream*>( &(ostringstream() << frame) )->str();
+// std::string py_string_0 = string("sim_step(").append(frame_str);
+// std::string py_string_1 = py_string_0.append(")\0");
+// PyRun_SimpleString(py_string_1.c_str());
+// }
+// if (fp != NULL){
+// fclose(fp);
+// }
+#endif
+
+ debMsg("Script finished.", 0);
+#ifdef GUI
+// guiWaitFinish();
+#endif
+
+ // finalize
+// Pb::finalize();
+ PyGILState_Release(gilstate);
+
+ delete [] cargs;
+}
+
+//int manta_main(int argc,char* argv[]) {
+// debMsg("Version: "<< buildInfoString() , 1);
+//
+//#ifdef GUI
+// guiMain(argc, argv);
+//#else
+// if (argc<=1) {
+// cerr << "Usage : Syntax is 'manta <config.py>'" << endl;
+// return 1;
+// }
+//
+// vector<string> args;
+// for (int i=1; i<argc; i++) args.push_back(argv[i]);
+// runMantaScript(args);
+//#endif
+//
+// return 0;
+//}
+#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/pwrapper/pythonInclude.h b/source/blender/python/manta_pp/pwrapper/pythonInclude.h new file mode 100755 index 00000000000..127fb92449f --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/pythonInclude.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Base class for particle systems + * + ******************************************************************************/ + +#ifndef _PYTHONINCLUDE_H +#define _PYTHONINCLUDE_H + +#if defined(WIN32) || defined(_WIN32) + + // note - we have to include these first! + #include <string> + #include <vector> + #include <iostream> + + #ifdef _DEBUG + + // special handling for windows + // disable linking with debug version of python libs + #undef _DEBUG + #define NDEBUG + #include <Python.h> + #define _DEBUG + #undef NDEBUG + + #else + #include <Python.h> + #endif +#else + #include <Python.h> +#endif + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/pwrapper/registry.cpp b/source/blender/python/manta_pp/pwrapper/registry.cpp new file mode 100644 index 00000000000..183ad9db778 --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/registry.cpp @@ -0,0 +1,681 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Auto python registry + * + ******************************************************************************/ + +#include "pythonInclude.h" +#include "structmember.h" +#include "manta.h" + +using namespace std; + +const string gDefaultModuleName = "manta"; + +namespace Pb { + +//****************************************************************************** +// Custom object definition + +struct Method { + Method(const string& n, const string& d, GenericFunction f) : name(n), doc(d), func(f) {} + string name, doc; + GenericFunction func; + + PyMethodDef def() { + PyMethodDef def = {&name[0], (PyCFunction)func, METH_VARARGS | METH_KEYWORDS, &doc[0]}; + return def; + } +}; +struct GetSet { + GetSet() : getter(0),setter(0) {} + GetSet(const string& n, const string& d, Getter g, Setter s) : name(n), doc(d), getter(g), setter(s) {} + string name, doc; + Getter getter; + Setter setter; + + PyGetSetDef def() { + PyGetSetDef def = {&name[0], getter, setter, &doc[0], NULL}; + return def; + } +}; + +struct ClassData { + string cName, pyName; + string cPureName, cTemplate; + InitFunc init; + PyTypeObject typeInfo; + PyNumberMethods numInfo; + //PySequenceMethods seqInfo; + vector<Method> methods; + map<string,GetSet> getset; + map<string,OperatorFunction> ops; + ClassData* baseclass; + string baseclassName; + Constructor constructor; + + vector<PyMethodDef> genMethods; + vector<PyGetSetDef> genGetSet; +}; + +struct PbObject { + PyObject_HEAD + Manta::PbClass *instance; + ClassData *classdef; +}; + +//****************************************************** +// Internal wrapper class + +//! Registers all classes and methods exposed to Python. +/*! This class is only used internally by Pb:: framwork. + * Please use the functionality of PbClass to lookup and translate pointers. */ +class WrapperRegistry { +public: + static WrapperRegistry& instance(); + void addClass(const std::string& name, const std::string& internalName, const std::string& baseclass); + void addExternalInitializer(InitFunc func); + void addMethod(const std::string& classname, const std::string& methodname, GenericFunction method); + void addOperator(const std::string& classname, const std::string& methodname, OperatorFunction method); + void addConstructor(const std::string& classname, Constructor method); + void addGetSet(const std::string& classname, const std::string& property, Getter getfunc, Setter setfunc); + void addPythonPath(const std::string& path); + void addPythonCode(const std::string& file, const std::string& code); + PyObject* createPyObject(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass *parent); + void construct(const std::string& scriptname, const vector<string>& args); + void construct_lite(); + void cleanup(); + void renameObjects(); + void runPreInit(); + PyObject* initModule(); + ClassData* lookup(const std::string& name); + bool canConvert(ClassData* from, ClassData* to); + void addScriptData(const std::string& scriptname, const vector<string>& args); +private: + ClassData* getOrConstructClass(const string& name); + void registerBaseclasses(); + void registerDummyTypes(); + void registerMeta(); + void addConstants(PyObject* module); + void registerOperators(ClassData* cls); + void addParentMethods(ClassData* cls, ClassData* base); + WrapperRegistry(); + std::map<std::string, ClassData*> mClasses; + std::vector<ClassData*> mClassList; + std::vector<InitFunc> mExtInitializers; + std::vector<std::string> mPaths; + std::string mCode, mScriptName; + std::vector<std::string> args; +}; + +//****************************************************************************** +// Callback functions + +PyObject* cbGetClass(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cPureName); +} + +PyObject* cbGetTemplate(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cTemplate); +} + +PyObject* cbGetCName(PbObject* self, void* cl) { + return Manta::toPy(self->classdef->cName); +} + +void cbDealloc(PbObject* self) { + //cout << "dealloc " << self->instance->getName() << " " << self->classdef->cName << endl; + if (self->instance) { + // don't delete top-level objects + if (self->instance->getParent() != self->instance) + delete self->instance; + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyObject* cbNew(PyTypeObject *type, PyObject *args, PyObject *kwds) { + PbObject *self = (PbObject*) type->tp_alloc(type, 0); + if (self != NULL) { + // lookup and link classdef + self->classdef = WrapperRegistry::instance().lookup(type->tp_name); + self->instance = NULL; + //cout << "creating " << self->classdef->cName << endl; + } else + errMsg("can't allocate new python class object"); + return (PyObject*) self; +} + +int cbDisableConstructor(PyObject* self, PyObject* args, PyObject* kwds) { + errMsg("Can't instantiate a class template without template arguments"); + return -1; +} + +PyMODINIT_FUNC PyInit_Main(void) { +#if PY_MAJOR_VERSION >= 3 + WrapperRegistry::instance().construct_lite(); + return WrapperRegistry::instance().initModule(); +#else + WrapperRegistry::instance().construct_lite(); + WrapperRegistry::instance().initModule(); +#endif +} + +PyMODINIT_FUNC PyInit_Main_Link(void) { +#if PY_MAJOR_VERSION >= 3 + return PyInit_Main(); +#else + PyInit_Main(); +#endif + +} + +PyObject *PyInit_Main_Obj(void) +{ + return PyInit_Main(); +} + +//****************************************************** +// WrapperRegistry + +WrapperRegistry::WrapperRegistry() { + addClass("__modclass__", "__modclass__" , ""); + addClass("PbClass", "PbClass", ""); +} + +ClassData* WrapperRegistry::getOrConstructClass(const string& classname) { + map<string,ClassData*>::iterator it = mClasses.find(classname); + + if (it != mClasses.end()) + return it->second; + ClassData* data = new ClassData; + data->cName = classname; + data->cPureName = classname; + data->cTemplate = ""; + size_t tplIdx = classname.find('<'); + if (tplIdx != string::npos) { + data->cPureName = classname.substr(0,tplIdx); + data->cTemplate = classname.substr(tplIdx+1, classname.find('>')-tplIdx-1); + } + data->baseclass = NULL; + data->constructor = cbDisableConstructor; + mClasses[classname] = data; + mClassList.push_back(data); + return data; +} + +void replaceAll(string& source, string const& find, string const& replace) { + for(string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;) + { + source.replace(i, find.length(), replace); + i += replace.length() - find.length() + 1; + } +} + +void WrapperRegistry::addClass(const string& pyName, const string& internalName, const string& baseclass) { + ClassData* data = getOrConstructClass(internalName); + + // regularize python name + string pythonName = pyName; + replaceAll(pythonName, "<", "_"); + replaceAll(pythonName, ">", ""); + replaceAll(pythonName, ",", "_"); + + if (data->pyName.empty()) + data->pyName = pythonName; + mClasses[pythonName] = data; + if (!baseclass.empty()) + data->baseclassName = baseclass; +} + +void WrapperRegistry::addExternalInitializer(InitFunc func) { + mExtInitializers.push_back(func); +} + +void WrapperRegistry::addPythonPath(const string& path) { + mPaths.push_back(path); +} + +void WrapperRegistry::addPythonCode(const string& file, const string& code) { + mCode += code + "\n"; +} + +void WrapperRegistry::addGetSet(const string& classname, const string& property, Getter getfunc, Setter setfunc) { + ClassData* classdef = getOrConstructClass(classname); + GetSet& def = classdef->getset[property]; + if (def.name.empty()) { + def.name = property; + def.doc = property; + } + if (getfunc) def.getter = getfunc; + if (setfunc) def.setter = setfunc; +} + +void WrapperRegistry::addMethod(const string& classname, const string& methodname, GenericFunction func) { + string aclass = classname; + if (aclass.empty()) + aclass = "__modclass__"; + + ClassData* classdef = getOrConstructClass(aclass); + for(int i=0; i<(int)classdef->methods.size(); i++) + if (classdef->methods[i].name == methodname) return; // avoid duplicates + classdef->methods.push_back(Method(methodname,methodname,func)); +} + +void WrapperRegistry::addOperator(const string& classname, const string& methodname, OperatorFunction func) { + if (classname.empty()) + errMsg("PYTHON operators have to be defined within classes."); + + string op = methodname.substr(8); + ClassData* classdef = getOrConstructClass(classname); + classdef->ops[op] = func; +} + +void WrapperRegistry::addConstructor(const string& classname, Constructor func) { + ClassData* classdef = getOrConstructClass(classname); + classdef->constructor = func; +} + +void WrapperRegistry::addParentMethods(ClassData* cur, ClassData* base) { + if (base == 0) return; + + for (vector<Method>::iterator it = base->methods.begin(); it != base->methods.end(); ++it) + addMethod(cur->cName, it->name, it->func); + + for (map<string,GetSet>::iterator it = base->getset.begin(); it != base->getset.end(); ++it) + addGetSet(cur->cName, it->first, it->second.getter, it->second.setter); + + for (map<string,OperatorFunction>::iterator it = base->ops.begin(); it != base->ops.end(); ++it) + cur->ops[it->first] = it->second; + + addParentMethods(cur, base->baseclass); +} + +void WrapperRegistry::registerBaseclasses() { + for (int i=0; i<(int)mClassList.size(); i++) { + string bname = mClassList[i]->baseclassName; + if(!bname.empty()) { + mClassList[i]->baseclass = lookup(bname); + if (!mClassList[i]->baseclass) + errMsg("Registering class '" + mClassList[i]->cName + "' : Base class '" + bname + "' not found"); + } + } + + for (int i=0; i<(int)mClassList.size(); i++) { + addParentMethods(mClassList[i], mClassList[i]->baseclass); + } +} + +void WrapperRegistry::registerMeta() { + for (int i=0; i<(int)mClassList.size(); i++) { + mClassList[i]->getset["_class"] = GetSet("_class", "C class name", (Getter)cbGetClass, 0); + mClassList[i]->getset["_cname"] = GetSet("_cname", "Full C name", (Getter)cbGetCName, 0); + mClassList[i]->getset["_T"] = GetSet("_T", "C template argument", (Getter)cbGetTemplate, 0); + } +} + +void WrapperRegistry::registerOperators(ClassData* cls) { + PyNumberMethods& num = cls->numInfo; + for (map<string,OperatorFunction>::iterator it = cls->ops.begin(); it != cls->ops.end(); it++) { + const string& op = it->first; + OperatorFunction func = it->second; + if (op=="+=") num.nb_inplace_add = func; + else if (op=="-=") num.nb_inplace_subtract = func; + else if (op=="*=") num.nb_inplace_multiply = func; + else if (op=="+") num.nb_add = func; + else if (op=="-") num.nb_subtract = func; + else if (op=="*") num.nb_multiply = func; +# if PY_MAJOR_VERSION < 3 + else if (op=="/=") num.nb_inplace_divide = func; + else if (op=="/") num.nb_divide = func; +# else + else if (op=="/=") num.nb_inplace_true_divide = func; + else if (op=="/") num.nb_true_divide = func; +# endif + else + errMsg("PYTHON operator " + op + " not supported"); + } +} + +void WrapperRegistry::registerDummyTypes() { + vector<string> add; + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + string cName = (*it)->cName; + if (cName.find('<') != string::npos) + add.push_back(cName.substr(0,cName.find('<'))); + } + for (int i=0; i<(int)add.size(); i++) + addClass(add[i],add[i],""); +} + +ClassData* WrapperRegistry::lookup(const string& name) { + for(map<string, ClassData*>::iterator it = mClasses.begin(); it != mClasses.end(); ++it) { + if (it->first == name || it->second->cName == name) + return it->second; + } + return NULL; +} + +void WrapperRegistry::cleanup() { + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + delete *it; + } + mClasses.clear(); + mClassList.clear(); +} + +WrapperRegistry& WrapperRegistry::instance() { + static WrapperRegistry inst; + return inst; +} + +bool WrapperRegistry::canConvert(ClassData* from, ClassData* to) { + if (from == to) return true; + if (from->baseclass) + return canConvert(from->baseclass, to); + return false; +} + +void WrapperRegistry::addConstants(PyObject* module) { + // expose arguments + PyObject* list = PyList_New(args.size()); + for (int i=0; i<(int)args.size(); i++) + PyList_SET_ITEM(list,i,Manta::toPy(args[i])); + PyModule_AddObject(module, "args", list); + PyModule_AddObject(module,"SCENEFILE",Manta::toPy(mScriptName)); + + // expose compile flags +#ifdef CUDA + PyModule_AddObject(module,"CUDA",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"CUDA",Manta::toPy<bool>(false)); +#endif +#ifdef DEBUG + PyModule_AddObject(module,"DEBUG",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"DEBUG",Manta::toPy<bool>(false)); +#endif +#ifdef MT + PyModule_AddObject(module,"MT",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"MT",Manta::toPy<bool>(false)); +#endif +#ifdef GUI + PyModule_AddObject(module,"GUI",Manta::toPy<bool>(true)); +#else + PyModule_AddObject(module,"GUI",Manta::toPy<bool>(false)); +#endif +} + +void WrapperRegistry::runPreInit() { + // add python directories to path + PyObject *sys_path = PySys_GetObject((char*)"path"); + for (size_t i=0; i<mPaths.size(); i++) { + PyObject *path = Manta::toPy(mPaths[i]); + if (sys_path == NULL || path == NULL || PyList_Append(sys_path, path) < 0) { + errMsg("unable to set python path"); + } + Py_DECREF(path); + } + if (!mCode.empty()) { + mCode = "from manta import *\n" + mCode; + PyRun_SimpleString(mCode.c_str()); + } +} + +PyObject* WrapperRegistry::createPyObject(const string& classname, const string& name, Manta::PbArgs& args, Manta::PbClass *parent) { + ClassData* classdef = lookup(classname); + if (!classdef) + errMsg("Class " + classname + " doesn't exist."); + + // create object + PyObject* obj = cbNew(&classdef->typeInfo, NULL, NULL); + PbObject* self = (PbObject*)obj; + PyObject* nkw = 0; + + if (args.kwds()) + nkw = PyDict_Copy(args.kwds()); + else + nkw = PyDict_New(); + + PyObject* nocheck = Py_BuildValue("s","yes"); + PyDict_SetItemString(nkw, "nocheck", nocheck); + if (parent) PyDict_SetItemString(nkw, "parent", parent->getPyObject()); + + // create instance + if (self->classdef->constructor(obj, args.linArgs(), nkw) < 0) + errMsg("error raised in constructor"); // assume condition is already set + + Py_DECREF(nkw); + Py_DECREF(nocheck); + self->instance->setName(name); + + return obj; +} + +// prepare typeinfo and register python module +void WrapperRegistry::construct(const string& scriptname, const vector<string>& args) { + mScriptName = scriptname; + this->args = args; + + registerBaseclasses(); + registerMeta(); + registerDummyTypes(); + + // load main extension module + PyImport_AppendInittab((char*)gDefaultModuleName.c_str(), PyInit_Main); +} +void WrapperRegistry::addScriptData(const std::string &scriptname, const vector<string> &args) +{ + mScriptName = scriptname; + this->args = args; +} + +void WrapperRegistry::construct_lite() { + registerBaseclasses(); + registerMeta(); + registerDummyTypes(); +} + +inline PyObject* castPy(PyTypeObject* p) { + return reinterpret_cast<PyObject*>(static_cast<void*>(p)); +} + +PyObject* WrapperRegistry::initModule() { + // generate and terminate all method lists + PyMethodDef sentinelFunc = { NULL, NULL, 0, NULL }; + PyGetSetDef sentinelGetSet = { NULL, NULL, NULL, NULL, NULL }; + for (int i=0; i<(int)mClassList.size(); i++) { + ClassData* cls = mClassList[i]; + cls->genMethods.clear(); + cls->genGetSet.clear(); + for (vector<Method>::iterator i2 = cls->methods.begin(); i2 != cls->methods.end(); ++i2) + cls->genMethods.push_back(i2->def()); + for (map<string,GetSet>::iterator i2 = cls->getset.begin(); i2 != cls->getset.end(); ++i2) + cls->genGetSet.push_back(i2->second.def()); + + cls->genMethods.push_back(sentinelFunc); + cls->genGetSet.push_back(sentinelGetSet); + } + + // prepare module info +#if PY_MAJOR_VERSION >= 3 + static PyModuleDef MainModule = { + PyModuleDef_HEAD_INIT, + gDefaultModuleName.c_str(), + "Bridge module to the C++ solver", + -1, + NULL, NULL, NULL, NULL, NULL + }; + // get generic methods (plugin functions) + MainModule.m_methods = &mClasses["__modclass__"]->genMethods[0]; + + // create module + PyObject* module = PyModule_Create(&MainModule); +#else + PyObject* module = Py_InitModule(gDefaultModuleName.c_str(), &mClasses["__modclass__"]->genMethods[0]); +#endif + if (module == NULL) + return NULL; + + // load classes + for(vector<ClassData*>::iterator it = mClassList.begin(); it != mClassList.end(); ++it) { + ClassData& data = **it; + char* nameptr = (char*)data.pyName.c_str(); + + // define numeric substruct + PyNumberMethods* num = 0; + if (!data.ops.empty()) { + num = &data.numInfo; + memset(num,0,sizeof(PyNumberMethods)); + registerOperators(&data); + } + + // define python classinfo + PyTypeObject t = { + PyVarObject_HEAD_INIT(NULL, 0) + (char*)data.pyName.c_str(),// tp_name + sizeof(PbObject), // tp_basicsize + 0, // tp_itemsize + (destructor)cbDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_reserved + 0, // tp_repr + num, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, // tp_flags + nameptr, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + &data.genMethods[0], // tp_methods + 0, // tp_members + &data.genGetSet[0], // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)(data.constructor),// tp_init + 0, // tp_alloc + cbNew // tp_new + }; + data.typeInfo = t; + + if (PyType_Ready(&data.typeInfo) < 0) + continue; + + for(map<string,ClassData*>::iterator i2 = mClasses.begin(); i2 != mClasses.end(); ++i2) { + if (*it != i2->second) continue; + // register all aliases + Py_INCREF(castPy(&data.typeInfo)); + PyModule_AddObject(module, (char*)i2->first.c_str(), (PyObject*) &data.typeInfo); + } + } + + // externals + for(vector<InitFunc>::iterator it = mExtInitializers.begin(); it != mExtInitializers.end(); ++it) { + (*it)(module); + } + + addConstants(module); + + return module; +} + + +//****************************************************** +// Register members and exposed functions + +void setup(const std::string& filename, const std::vector<std::string>& args) { +// WrapperRegistry::instance().construct(filename,args); +// Py_Initialize(); + WrapperRegistry::instance().addScriptData(filename, args); + WrapperRegistry::instance().runPreInit(); +} + +void finalize() { +// Py_Finalize(); + WrapperRegistry::instance().cleanup(); +} + +bool canConvert(PyObject* obj, const string& classname) { + ClassData* from = ((PbObject*)obj)->classdef; + ClassData* dest = WrapperRegistry::instance().lookup(classname); + if (!dest) + errMsg("Classname '" + classname + "' is not registered."); + return WrapperRegistry::instance().canConvert(from, dest); +} + +Manta::PbClass* objFromPy(PyObject* obj) { + if (Py_TYPE(obj)->tp_dealloc != (destructor)cbDealloc) // not a manta object + return NULL; + + return ((PbObject*) obj)->instance; +} + +PyObject* copyObject(Manta::PbClass* cls, const string& classname) { + ClassData* classdef = WrapperRegistry::instance().lookup(classname); + assertMsg(classdef,"python class " + classname + " does not exist."); + + // allocate new object + PbObject *obj = (PbObject*) classdef->typeInfo.tp_alloc(&(classdef->typeInfo), 0); + assertMsg(obj, "cannot allocate new python object"); + + obj->classdef = classdef; + cls->registerObject((PyObject*)obj, 0); + + return cls->getPyObject(); +} + +Manta::PbClass* createPy(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass* parent) { + PyObject* obj = WrapperRegistry::instance().createPyObject(classname, name, args, parent); + return ((PbObject*)obj)->instance; +} + +void setReference(Manta::PbClass* cls, PyObject* obj) { + ((PbObject*) obj)->instance = cls; +} + +Register::Register(const string& className, const string& funcName, GenericFunction func) { + WrapperRegistry::instance().addMethod(className, funcName, func); +} +Register::Register(const string& className, const string& funcName, OperatorFunction func) { + WrapperRegistry::instance().addOperator(className, funcName, func); +} +Register::Register(const string& className, const string& funcName, Constructor func) { + WrapperRegistry::instance().addConstructor(className, func); +} +Register::Register(const string& className, const string& property, Getter getter, Setter setter) { + WrapperRegistry::instance().addGetSet(className, property, getter, setter); +} +Register::Register(const string& className, const string& pyName, const string& baseClass) { + WrapperRegistry::instance().addClass(pyName, className, baseClass); +} +Register::Register(const string& file, const string& pythonCode) { + WrapperRegistry::instance().addPythonCode(file, pythonCode); +} +Register::Register(InitFunc func) { + WrapperRegistry::instance().addExternalInitializer(func); +} + +} // namespace
\ No newline at end of file diff --git a/source/blender/python/manta_pp/pwrapper/registry.h b/source/blender/python/manta_pp/pwrapper/registry.h new file mode 100644 index 00000000000..75d5a272f90 --- /dev/null +++ b/source/blender/python/manta_pp/pwrapper/registry.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2014 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Auto python registry + * + ******************************************************************************/ + +#ifndef _REGISTRY_H +#define _REGISTRY_H + +#include <string> +#include <vector> + +// forward declaration to minimize Python.h includes +#ifndef PyObject_HEAD +#ifndef PyObject_Fake +struct _object; +typedef _object PyObject; +#define PyObject_Fake +#endif +#endif + +namespace Manta { + class PbClass; + class PbArgs; +} + +// ************************************************** +// NOTE +// Everything in this file is intend only for internal +// use by the generated wrappers or pclass/pconvert. +// For user code, use the functionality exposed in +// pclass.h / pconvert.h instead. +// ************************************************** + +// Used to turn names into strings +namespace Manta { +template<class T> struct Namify { + static const char* S; +}; +} +namespace Pb { + +// internal registry access +void setup(const std::string& filename, const std::vector<std::string>& args); +void finalize(); +bool canConvert(PyObject* obj, const std::string& to); +Manta::PbClass* objFromPy(PyObject* obj); +Manta::PbClass* createPy(const std::string& classname, const std::string& name, Manta::PbArgs& args, Manta::PbClass* parent); +void setReference(Manta::PbClass* cls, PyObject* obj); +PyObject* copyObject(Manta::PbClass* cls, const std::string& classname); + +// callback type +typedef void (*InitFunc)(PyObject*); +extern "C" PyObject *PyInit_Main_Obj(void); +typedef PyObject* (*GenericFunction)(PyObject* self, PyObject* args, PyObject* kwds); +typedef PyObject* (*OperatorFunction)(PyObject* self, PyObject* o); +typedef int (*Constructor)(PyObject* self, PyObject* args, PyObject* kwds); +typedef PyObject* (*Getter)(PyObject* self, void* closure); +typedef int (*Setter)(PyObject* self, PyObject* value, void* closure); + +//! Auto registry of python methods and classes +struct Register { + //! register method + Register(const std::string& className, const std::string& funcName, GenericFunction func); + //! register operator + Register(const std::string& className, const std::string& funcName, OperatorFunction func); + //! register constructor + Register(const std::string& className, const std::string& funcName, Constructor func); + //! register getter/setter + Register(const std::string& className, const std::string& property, Getter getter, Setter setter); + //! register class + Register(const std::string& className, const std::string& pyName, const std::string& baseClass); + //! register python code + Register(const std::string& file, const std::string& pythonCode); + //! register external code + Register(InitFunc func); +}; + +} +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/python/defines.py b/source/blender/python/manta_pp/python/defines.py new file mode 100644 index 00000000000..1c7f01ab034 --- /dev/null +++ b/source/blender/python/manta_pp/python/defines.py @@ -0,0 +1,11 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + diff --git a/source/blender/python/manta_pp/python/defines.py.reg b/source/blender/python/manta_pp/python/defines.py.reg new file mode 100644 index 00000000000..6ef1bc7fceb --- /dev/null +++ b/source/blender/python/manta_pp/python/defines.py.reg @@ -0,0 +1,2 @@ +#include "registry.h" +static const Pb::Register _reg("python/defines.py", "################################################################################\n#\n# MantaFlow fluid solver framework\n# Copyright 2011 Tobias Pfaff, Nils Thuerey \n#\n# This program is free software, distributed under the terms of the\n# GNU General Public License (GPL) \n# http://www.gnu.org/licenses\n#\n# Defines some constants for use in python subprograms\n#\n#################################################################################\n\n# mantaflow conventions\nReal = float\n\n# grid flags\nFlagFluid = 1\nFlagObstacle = 2\nFlagEmpty = 4\nFlagStick = 128\nFlagReserved = 256\n\n# integration mode\nIntEuler = 0\nIntRK2 = 1\nIntRK4 = 2\n\n\n\n"); diff --git a/source/blender/python/manta_pp/python/defines.py.reg.cpp b/source/blender/python/manta_pp/python/defines.py.reg.cpp new file mode 100644 index 00000000000..6ef1bc7fceb --- /dev/null +++ b/source/blender/python/manta_pp/python/defines.py.reg.cpp @@ -0,0 +1,2 @@ +#include "registry.h" +static const Pb::Register _reg("python/defines.py", "################################################################################\n#\n# MantaFlow fluid solver framework\n# Copyright 2011 Tobias Pfaff, Nils Thuerey \n#\n# This program is free software, distributed under the terms of the\n# GNU General Public License (GPL) \n# http://www.gnu.org/licenses\n#\n# Defines some constants for use in python subprograms\n#\n#################################################################################\n\n# mantaflow conventions\nReal = float\n\n# grid flags\nFlagFluid = 1\nFlagObstacle = 2\nFlagEmpty = 4\nFlagStick = 128\nFlagReserved = 256\n\n# integration mode\nIntEuler = 0\nIntRK2 = 1\nIntRK4 = 2\n\n\n\n"); diff --git a/source/blender/python/manta_pp/shapes.cpp b/source/blender/python/manta_pp/shapes.cpp new file mode 100644 index 00000000000..6210f4c4ec3 --- /dev/null +++ b/source/blender/python/manta_pp/shapes.cpp @@ -0,0 +1,394 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Shape classes + * + ******************************************************************************/ + +#include "shapes.h" +#include "commonkernels.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +//****************************************************************************** +// Shape class members + +Shape::Shape (FluidSolver* parent) + : PbClass(parent), mType(TypeNone) +{ +} + +LevelsetGrid Shape::computeLevelset() { + // note - 3d check deactivated! TODO double check... + LevelsetGrid phi(getParent()); + generateLevelset(phi); + return phi; +} + +bool Shape::isInside(const Vec3& pos) const { + return false; +} + +//! Kernel: Apply a shape to a grid, setting value inside + +template <class T> struct ApplyShapeToGrid : public KernelBase { ApplyShapeToGrid(Grid<T>* grid, Shape* shape, T value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),shape(shape),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, Grid<T>* grid, Shape* shape, T value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (shape->isInsideGrid(i,j,k)) + (*grid)(i,j,k) = value; +} inline Grid<T>* getArg0() { return grid; } typedef Grid<T> type0;inline Shape* getArg1() { return shape; } typedef Shape type1;inline T& getArg2() { return value; } typedef T type2;inline FlagGrid* getArg3() { return respectFlags; } typedef FlagGrid type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,shape,value,respectFlags); } Grid<T>* grid; Shape* shape; T value; FlagGrid* respectFlags; }; + +//! Kernel: Apply a shape to a grid, setting value inside (scaling by SDF value) + +template <class T> struct ApplyShapeToGridSmooth : public KernelBase { ApplyShapeToGridSmooth(Grid<T>* grid, Grid<Real>& phi, Real sigma, Real shift, T value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),phi(phi),sigma(sigma),shift(shift),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, Grid<T>* grid, Grid<Real>& phi, Real sigma, Real shift, T value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + const Real p = phi(i,j,k) - shift; + if (p < -sigma) + (*grid)(i,j,k) = value; + else if (p < sigma) + (*grid)(i,j,k) = value*(0.5f*(1.0f-p/sigma)); +} inline Grid<T>* getArg0() { return grid; } typedef Grid<T> type0;inline Grid<Real>& getArg1() { return phi; } typedef Grid<Real> type1;inline Real& getArg2() { return sigma; } typedef Real type2;inline Real& getArg3() { return shift; } typedef Real type3;inline T& getArg4() { return value; } typedef T type4;inline FlagGrid* getArg5() { return respectFlags; } typedef FlagGrid type5; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,phi,sigma,shift,value,respectFlags); } Grid<T>* grid; Grid<Real>& phi; Real sigma; Real shift; T value; FlagGrid* respectFlags; }; + +//! Kernel: Apply a shape to a MAC grid, setting value inside + + struct ApplyShapeToMACGrid : public KernelBase { ApplyShapeToMACGrid(MACGrid* grid, Shape* shape, Vec3 value, FlagGrid* respectFlags) : KernelBase(grid,0) ,grid(grid),shape(shape),value(value),respectFlags(respectFlags) { run(); } inline void op(int i, int j, int k, MACGrid* grid, Shape* shape, Vec3 value, FlagGrid* respectFlags ) { + if (respectFlags && respectFlags->isObstacle(i,j,k)) + return; + if (shape->isInside(Vec3(i,j+0.5,k+0.5))) (*grid)(i,j,k).x = value.x; + if (shape->isInside(Vec3(i+0.5,j,k+0.5))) (*grid)(i,j,k).y = value.y; + if (shape->isInside(Vec3(i+0.5,j+0.5,k))) (*grid)(i,j,k).z = value.z; +} inline MACGrid* getArg0() { return grid; } typedef MACGrid type0;inline Shape* getArg1() { return shape; } typedef Shape type1;inline Vec3& getArg2() { return value; } typedef Vec3 type2;inline FlagGrid* getArg3() { return respectFlags; } typedef FlagGrid type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, grid,shape,value,respectFlags); } MACGrid* grid; Shape* shape; Vec3 value; FlagGrid* respectFlags; }; + +void Shape::applyToGrid(GridBase* grid, FlagGrid* respectFlags) { + if (grid->getType() & GridBase::TypeInt) + ApplyShapeToGrid<int> ((Grid<int>*)grid, this, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyShapeToGrid<Real> ((Grid<Real>*)grid, this, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeMAC) + ApplyShapeToMACGrid ((MACGrid*)grid, this, _args.get<Vec3>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyShapeToGrid<Vec3> ((Grid<Vec3>*)grid, this, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGrid(): unknown grid type"); +} + +void Shape::applyToGridSmooth(GridBase* grid, Real sigma, Real shift, FlagGrid* respectFlags) { + Grid<Real> phi(grid->getParent()); + generateLevelset(phi); + + if (grid->getType() & GridBase::TypeInt) + ApplyShapeToGridSmooth<int> ((Grid<int>*)grid, phi, sigma, shift, _args.get<int>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeReal) + ApplyShapeToGridSmooth<Real> ((Grid<Real>*)grid, phi, sigma, shift, _args.get<Real>("value"), respectFlags); + else if (grid->getType() & GridBase::TypeVec3) + ApplyShapeToGridSmooth<Vec3> ((Grid<Vec3>*)grid, phi, sigma, shift, _args.get<Vec3>("value"), respectFlags); + else + errMsg("Shape::applyToGridSmooth(): unknown grid type"); +} + +void Shape::collideMesh(Mesh& mesh) { + const Real margin = 0.2; + + Grid<Real> phi(getParent()); + Grid<Vec3> grad(getParent()); + generateLevelset(phi); + GradientOp(grad, phi); + + const int num=mesh.numNodes(); + for(int i=0; i<num; i++) { + const Vec3& p = mesh.nodes(i).pos; + mesh.nodes(i).flags &= ~(Mesh::NfCollide | Mesh::NfMarked); + if (!phi.isInBounds(p,1)) continue; + + for (int iter=0; iter<10; iter++) { + const Real dist= phi.getInterpolated(p); + if (dist<margin) { + Vec3 n = grad.getInterpolated(p); + normalize(n); + mesh.nodes(i).pos += (margin-dist) * n; + mesh.nodes(i).flags |= Mesh::NfCollide | Mesh::NfMarked; + } + else break; + } + } +} + +//****************************************************************************** +// Derived shape class members + +Box::Box(FluidSolver* parent, Vec3 center, Vec3 p0, Vec3 p1, Vec3 size) + : Shape(parent) +{ + mType = TypeBox; + if (center.isValid() && size.isValid()) { + mP0 = center - size; + mP1 = center + size; + } else if (p0.isValid() && p1.isValid()) { + mP0 = p0; + mP1 = p1; + } else + errMsg("Box: specify either p0,p1 or size,center"); + +} + +bool Box::isInside(const Vec3& pos) const { + return (pos.x >= mP0.x && pos.y >= mP0.y && pos.z >= mP0.z && + pos.x <= mP1.x && pos.y <= mP1.y && pos.z <= mP1.z); +} + +void Box::generateMesh(Mesh* mesh) { + const int quadidx[24] = { 0,4,6,2, 3,7,5,1, 0,1,5,4, 6,7,3,2, 0,2,3,1, 5,7,6,4 }; + const int nodebase = mesh->numNodes(); + int oldtri = mesh->numTris(); + for (int i=0; i<8; i++) { + Node p; + p.flags = 0; + p.pos = mP0; + if (i&1) p.pos.x=mP1.x; + if (i&2) p.pos.y=mP1.y; + if (i&4) p.pos.z=mP1.z; + mesh->addNode(p); + } + for (int i=0; i<6; i++) { + mesh->addTri(Triangle(nodebase + quadidx[i*4+0], nodebase + quadidx[i*4+1], nodebase + quadidx[i*4+3])); + mesh->addTri(Triangle(nodebase + quadidx[i*4+1], nodebase + quadidx[i*4+2], nodebase + quadidx[i*4+3])); + } + mesh->rebuildCorners(oldtri,-1); + mesh->rebuildLookup(oldtri,-1); +} + +//! Kernel: Analytic SDF for box shape + struct BoxSDF : public KernelBase { BoxSDF(Grid<Real>& phi, const Vec3& p1, const Vec3& p2) : KernelBase(&phi,0) ,phi(phi),p1(p1),p2(p2) { run(); } inline void op(int i, int j, int k, Grid<Real>& phi, const Vec3& p1, const Vec3& p2 ) { + const Vec3 p(i+0.5, j+0.5, k+0.5); + if (p.x <= p2.x && p.x >= p1.x && p.y <= p2.y && p.y >= p1.y && p.z <= p2.z && p.z >= p1.z) { + // inside: minimal surface distance + Real mx = max(p.x-p2.x, p1.x-p.x); + Real my = max(p.y-p2.y, p1.y-p.y); + Real mz = max(p.z-p2.z, p1.z-p.z); + phi(i,j,k) = max(mx,max(my,mz)); + } else if (p.y <= p2.y && p.y >= p1.y && p.z <= p2.z && p.z >= p1.z) { + // outside plane X + phi(i,j,k) = max(p.x-p2.x, p1.x-p.x); + } else if (p.x <= p2.x && p.x >= p1.x && p.z <= p2.z && p.z >= p1.z) { + // outside plane Y + phi(i,j,k) = max(p.y-p2.y, p1.y-p.y); + } else if (p.x <= p2.x && p.x >= p1.x && p.y <= p2.y && p.y >= p1.y) { + // outside plane Z + phi(i,j,k) = max(p.z-p2.z, p1.z-p.z); + } else if (p.x > p1.x && p.x < p2.x) { + // lines X + Real m1 = sqrt(square(p1.y-p.y)+square(p1.z-p.z)); + Real m2 = sqrt(square(p2.y-p.y)+square(p1.z-p.z)); + Real m3 = sqrt(square(p1.y-p.y)+square(p2.z-p.z)); + Real m4 = sqrt(square(p2.y-p.y)+square(p2.z-p.z)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else if (p.y > p1.y && p.y < p2.y) { + // lines Y + Real m1 = sqrt(square(p1.x-p.x)+square(p1.z-p.z)); + Real m2 = sqrt(square(p2.x-p.x)+square(p1.z-p.z)); + Real m3 = sqrt(square(p1.x-p.x)+square(p2.z-p.z)); + Real m4 = sqrt(square(p2.x-p.x)+square(p2.z-p.z)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else if (p.z > p1.x && p.z < p2.z) { + // lines Z + Real m1 = sqrt(square(p1.y-p.y)+square(p1.x-p.x)); + Real m2 = sqrt(square(p2.y-p.y)+square(p1.x-p.x)); + Real m3 = sqrt(square(p1.y-p.y)+square(p2.x-p.x)); + Real m4 = sqrt(square(p2.y-p.y)+square(p2.x-p.x)); + phi(i,j,k) = min(m1,min(m2,min(m3,m4))); + } else { + // points + Real m = norm(p-Vec3(p1.x,p1.y,p1.z)); + m = min(m, norm(p-Vec3(p1.x,p1.y,p2.z))); + m = min(m, norm(p-Vec3(p1.x,p2.y,p1.z))); + m = min(m, norm(p-Vec3(p1.x,p2.y,p2.z))); + m = min(m, norm(p-Vec3(p2.x,p1.y,p1.z))); + m = min(m, norm(p-Vec3(p2.x,p1.y,p2.z))); + m = min(m, norm(p-Vec3(p2.x,p2.y,p1.z))); + m = min(m, norm(p-Vec3(p2.x,p2.y,p2.z))); + phi(i,j,k) = m; + } +} inline Grid<Real>& getArg0() { return phi; } typedef Grid<Real> type0;inline const Vec3& getArg1() { return p1; } typedef Vec3 type1;inline const Vec3& getArg2() { return p2; } typedef Vec3 type2; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, phi,p1,p2); } Grid<Real>& phi; const Vec3& p1; const Vec3& p2; }; +void Box::generateLevelset(Grid<Real>& phi) { + BoxSDF(phi, mP0, mP1); +} + +Sphere::Sphere (FluidSolver* parent, Vec3 center, Real radius, Vec3 scale) + : Shape(parent), mCenter(center), mScale(scale), mRadius(radius) +{ + mType = TypeSphere; +} + +bool Sphere::isInside(const Vec3& pos) const { + return normSquare((pos - mCenter) / mScale) <= mRadius * mRadius; +} + +struct Tri { Vec3 t[3]; int i[3]; Tri(Vec3 a,Vec3 b, Vec3 c) {t[0]=a;t[1]=b;t[2]=c;}}; +void Sphere::generateMesh(Mesh* mesh) { + vector<Tri> tris; + const int iterations = 3; + int oldtri = mesh->numTris(); + + // start with octahedron + const Real d = sqrt(0.5); + Vec3 p[6] = {Vec3(0,1,0), Vec3(0,-1,0), Vec3(-d,0,-d), Vec3(d,0,-d), Vec3(d,0,d), Vec3(-d,0,d)}; + tris.push_back(Tri(p[0],p[4],p[3])); + tris.push_back(Tri(p[0],p[5],p[4])); + tris.push_back(Tri(p[0],p[2],p[5])); + tris.push_back(Tri(p[0],p[3],p[2])); + tris.push_back(Tri(p[1],p[3],p[4])); + tris.push_back(Tri(p[1],p[4],p[5])); + tris.push_back(Tri(p[1],p[5],p[2])); + tris.push_back(Tri(p[1],p[2],p[3])); + + // Bisect each edge and move to the surface of a unit sphere + for (int it=0; it<iterations; it++) { + int ntold = tris.size(); + for (int i=0; i<ntold; i++) { + Vec3 pa = 0.5 * (tris[i].t[0] + tris[i].t[1]); + Vec3 pb = 0.5 * (tris[i].t[1] + tris[i].t[2]); + Vec3 pc = 0.5 * (tris[i].t[2] + tris[i].t[0]); + normalize(pa); normalize(pb); normalize(pc); + + tris.push_back(Tri(tris[i].t[0], pa, pc)); + tris.push_back(Tri(pa, tris[i].t[1], pb)); + tris.push_back(Tri(pb, tris[i].t[2], pc)); + tris[i].t[0] = pa; + tris[i].t[1] = pb; + tris[i].t[2] = pc; + } + } + + // index + scale + vector<Vec3> nodes; + for (size_t i=0; i<tris.size(); i++) { + for (int t=0; t<3; t++) { + Vec3 p = mCenter + tris[i].t[t] * mRadius * mScale; + // vector already there ? + int idx=nodes.size(); + for (size_t j=0; j<nodes.size(); j++) { + if (p==nodes[j]) { + idx = j; break; + } + } + if (idx == (int)nodes.size()) + nodes.push_back(p); + tris[i].i[t] = idx; + } + } + + // add the to mesh + const int ni = mesh->numNodes(); + for (size_t i=0; i<nodes.size(); i++) { + mesh->addNode(Node(nodes[i]));} + for (size_t t=0; t<tris.size(); t++) + mesh->addTri(Triangle(tris[t].i[0]+ni, tris[t].i[1]+ni, tris[t].i[2]+ni)); + + mesh->rebuildCorners(oldtri,-1); + mesh->rebuildLookup(oldtri,-1); +} + + struct SphereSDF : public KernelBase { SphereSDF(Grid<Real>& phi, Vec3 center, Real radius, Vec3 scale) : KernelBase(&phi,0) ,phi(phi),center(center),radius(radius),scale(scale) { run(); } inline void op(int i, int j, int k, Grid<Real>& phi, Vec3 center, Real radius, Vec3 scale ) { + phi(i,j,k) = norm((Vec3(i+0.5,j+0.5,k+0.5)-center)/scale)-radius; +} inline Grid<Real>& getArg0() { return phi; } typedef Grid<Real> type0;inline Vec3& getArg1() { return center; } typedef Vec3 type1;inline Real& getArg2() { return radius; } typedef Real type2;inline Vec3& getArg3() { return scale; } typedef Vec3 type3; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, phi,center,radius,scale); } Grid<Real>& phi; Vec3 center; Real radius; Vec3 scale; }; +void Sphere::generateLevelset(Grid<Real>& phi) { + SphereSDF(phi, mCenter, mRadius, mScale); +} + +Cylinder::Cylinder(FluidSolver* parent, Vec3 center, Real radius, Vec3 z) + : Shape(parent), mCenter(center), mRadius(radius) +{ + mType = TypeCylinder; + mZDir = z; + mZ = normalize(mZDir); +} + +bool Cylinder::isInside(const Vec3& pos) const { + Real z = dot(pos-mCenter, mZDir); + if (fabs(z) > mZ) return false; + Real r2 = normSquare(pos-mCenter)-square(z); + return r2 < square(mRadius); +} + +void Cylinder::generateMesh(Mesh* mesh) { + // generate coordinate system + Vec3 x = getOrthogonalVector(mZDir)*mRadius; + Vec3 y = cross(x, mZDir); + Vec3 z = mZDir*mZ; + int oldtri = mesh->numTris(); + + // construct node ring + const int N = 20; + const int base = mesh->numNodes(); + for (int i=0;i<N;i++) { + const Real phi = 2.0*M_PI*(Real)i/(Real)N; + Vec3 r = x*cos(phi) + y*sin(phi) + mCenter; + mesh->addNode(Node(r+z)); + mesh->addNode(Node(r-z)); + } + // top/bottom center + mesh->addNode(Node(mCenter+z)); + mesh->addNode(Node(mCenter-z)); + + // connect with tris + for (int i=0;i<N;i++) { + int cur = base+2*i; + int next = base+2*((i+1)%N); + // outside + mesh->addTri(Triangle(cur, next, cur+1)); + mesh->addTri(Triangle(next, next+1, cur+1)); + // upper / lower + mesh->addTri(Triangle(cur,base+2*N,next)); + mesh->addTri(Triangle(cur+1,next+1,base+2*N+1)); + } + + mesh->rebuildCorners(oldtri, -1); + mesh->rebuildLookup(oldtri,-1); +} + + + struct CylinderSDF : public KernelBase { CylinderSDF(Grid<Real>& phi, Vec3 center, Real radius, Vec3 zaxis, Real maxz) : KernelBase(&phi,0) ,phi(phi),center(center),radius(radius),zaxis(zaxis),maxz(maxz) { run(); } inline void op(int i, int j, int k, Grid<Real>& phi, Vec3 center, Real radius, Vec3 zaxis, Real maxz ) { + Vec3 p=Vec3(i+0.5,j+0.5,k+0.5)-center; + Real z = fabs(dot(p, zaxis)); + Real r = sqrt(normSquare(p)-z*z); + if (z < maxz) { + // cylinder z area + if (r < radius) + phi(i,j,k) = max(r-radius,z-maxz); + else + phi(i,j,k) = r-radius; + } else if (r < radius) { + // cylinder top area + phi(i,j,k) = fabs(z-maxz); + } else { + // edge + phi(i,j,k) = sqrt(square(z-maxz)+square(r-radius)); + } +} inline Grid<Real>& getArg0() { return phi; } typedef Grid<Real> type0;inline Vec3& getArg1() { return center; } typedef Vec3 type1;inline Real& getArg2() { return radius; } typedef Real type2;inline Vec3& getArg3() { return zaxis; } typedef Vec3 type3;inline Real& getArg4() { return maxz; } typedef Real type4; void run() { const int _maxX = maxX; const int _maxY = maxY; for (int k=minZ; k< maxZ; k++) for (int j=0; j< _maxY; j++) for (int i=0; i< _maxX; i++) op(i,j,k, phi,center,radius,zaxis,maxz); } Grid<Real>& phi; Vec3 center; Real radius; Vec3 zaxis; Real maxz; }; +void Cylinder::generateLevelset(Grid<Real>& phi) { + CylinderSDF(phi, mCenter, mRadius, mZDir, mZ); +} + +} //namespace + + diff --git a/source/blender/python/manta_pp/shapes.h b/source/blender/python/manta_pp/shapes.h new file mode 100644 index 00000000000..b5f003985ba --- /dev/null +++ b/source/blender/python/manta_pp/shapes.h @@ -0,0 +1,142 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * shapes classes + * + ******************************************************************************/ + +#ifndef _SHAPES_H +#define _SHAPES_H + +#include "manta.h" +#include "vectorbase.h" +#include "levelset.h" + +namespace Manta { + +// forward declaration +class Mesh; + +//! Base class for all shapes +class Shape : public PbClass {public: + enum GridType { TypeNone = 0, TypeBox = 1, TypeSphere = 2, TypeCylinder }; + + Shape(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Shape::Shape" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new Shape(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Shape::Shape" ); return 0; } catch(std::exception& e) { pbSetError("Shape::Shape",e.what()); return -1; } } + + //! Get the type of grid + inline GridType getType() const { return mType; } + + //! Apply shape to flag grid, set inside cells to <value> + void applyToGrid(GridBase* grid, FlagGrid* respectFlags=0); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::applyToGrid"); PyObject *_retval = 0; { ArgLocker _lock; GridBase* grid = _args.getPtr<GridBase >("grid",0,&_lock); FlagGrid* respectFlags = _args.getPtrOpt<FlagGrid >("respectFlags",1,0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->applyToGrid(grid,respectFlags); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::applyToGrid"); return _retval; } catch(std::exception& e) { pbSetError("Shape::applyToGrid",e.what()); return 0; } } + void applyToGridSmooth(GridBase* grid, Real sigma=1.0, Real shift=0, FlagGrid* respectFlags=0); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::applyToGridSmooth"); PyObject *_retval = 0; { ArgLocker _lock; GridBase* grid = _args.getPtr<GridBase >("grid",0,&_lock); Real sigma = _args.getOpt<Real >("sigma",1,1.0,&_lock); Real shift = _args.getOpt<Real >("shift",2,0,&_lock); FlagGrid* respectFlags = _args.getPtrOpt<FlagGrid >("respectFlags",3,0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->applyToGridSmooth(grid,sigma,shift,respectFlags); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::applyToGridSmooth"); return _retval; } catch(std::exception& e) { pbSetError("Shape::applyToGridSmooth",e.what()); return 0; } } + LevelsetGrid computeLevelset(); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::computeLevelset"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->computeLevelset()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::computeLevelset"); return _retval; } catch(std::exception& e) { pbSetError("Shape::computeLevelset",e.what()); return 0; } } + void collideMesh(Mesh& mesh); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::collideMesh"); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->collideMesh(mesh); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::collideMesh"); return _retval; } catch(std::exception& e) { pbSetError("Shape::collideMesh",e.what()); return 0; } } + virtual Vec3 getCenter() const { return Vec3::Zero; } static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::getCenter"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getCenter()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::getCenter"); return _retval; } catch(std::exception& e) { pbSetError("Shape::getCenter",e.what()); return 0; } } + virtual void setCenter(const Vec3& center) {} static PyObject* _W_6 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::setCenter"); PyObject *_retval = 0; { ArgLocker _lock; const Vec3& center = _args.get<Vec3 >("center",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setCenter(center); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::setCenter"); return _retval; } catch(std::exception& e) { pbSetError("Shape::setCenter",e.what()); return 0; } } + virtual Vec3 getExtent() const { return Vec3::Zero; } static PyObject* _W_7 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Shape* pbo = dynamic_cast<Shape*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Shape::getExtent"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = toPy(pbo->getExtent()); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Shape::getExtent"); return _retval; } catch(std::exception& e) { pbSetError("Shape::getExtent",e.what()); return 0; } } + + //! Inside test of the shape + virtual bool isInside(const Vec3& pos) const; + inline bool isInsideGrid(int i, int j, int k) const { return isInside(Vec3(i+0.5,j+0.5,k+0.5)); }; + + virtual void generateMesh(Mesh* mesh) {} ; + virtual void generateLevelset(Grid<Real>& phi) {}; + +protected: GridType mType; public: PbArgs _args;} +#define _C_Shape +; + +//! Dummy shape +class NullShape : public Shape { +public: + NullShape(FluidSolver* parent) :Shape(parent){} static int _W_8 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "NullShape::NullShape" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new NullShape(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"NullShape::NullShape" ); return 0; } catch(std::exception& e) { pbSetError("NullShape::NullShape",e.what()); return -1; } } + + virtual bool isInside(const Vec3& pos) const { return false; } + virtual void generateMesh(Mesh* mesh) {} + +protected: virtual void generateLevelset(Grid<Real>& phi) { gridSetConst<Real>( phi , 1000.0f ); } public: PbArgs _args;} +#define _C_NullShape +; + +//! Box shape +class Box : public Shape { +public: + Box(FluidSolver* parent, Vec3 center = Vec3::Invalid, Vec3 p0 = Vec3::Invalid, Vec3 p1 = Vec3::Invalid, Vec3 size = Vec3::Invalid); static int _W_9 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Box::Box" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); Vec3 center = _args.getOpt<Vec3 >("center",1,Vec3::Invalid,&_lock); Vec3 p0 = _args.getOpt<Vec3 >("p0",2,Vec3::Invalid,&_lock); Vec3 p1 = _args.getOpt<Vec3 >("p1",3,Vec3::Invalid,&_lock); Vec3 size = _args.getOpt<Vec3 >("size",4,Vec3::Invalid,&_lock); obj = new Box(parent,center,p0,p1,size); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Box::Box" ); return 0; } catch(std::exception& e) { pbSetError("Box::Box",e.what()); return -1; } } + + inline Vec3 getSize() const { return mP1-mP0; } + inline Vec3 getP0() const { return mP0; } + inline Vec3 getP1() const { return mP1; } + virtual void setCenter(const Vec3& center) { Vec3 dh=0.5*(mP1-mP0); mP0 = center-dh; mP1 = center+dh;} + virtual Vec3 getCenter() const { return 0.5*(mP1+mP0); } + virtual Vec3 getExtent() const { return getSize(); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: Vec3 mP0, mP1; public: PbArgs _args;} +#define _C_Box +; + +//! Spherical shape +class Sphere : public Shape { +public: + Sphere(FluidSolver* parent, Vec3 center, Real radius, Vec3 scale=Vec3(1,1,1)); static int _W_10 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Sphere::Sphere" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); Vec3 center = _args.get<Vec3 >("center",1,&_lock); Real radius = _args.get<Real >("radius",2,&_lock); Vec3 scale = _args.getOpt<Vec3 >("scale",3,Vec3(1,1,1),&_lock); obj = new Sphere(parent,center,radius,scale); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Sphere::Sphere" ); return 0; } catch(std::exception& e) { pbSetError("Sphere::Sphere",e.what()); return -1; } } + + virtual void setCenter(const Vec3& center) { mCenter = center; } + virtual Vec3 getCenter() const { return mCenter; } + inline Real getRadius() const { return mRadius; } + virtual Vec3 getExtent() const { return Vec3(2.0*mRadius); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: + Vec3 mCenter, mScale; Real mRadius; public: PbArgs _args;} +#define _C_Sphere +; + +//! Cylindrical shape +class Cylinder : public Shape { +public: + Cylinder(FluidSolver* parent, Vec3 center, Real radius, Vec3 z); static int _W_11 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Cylinder::Cylinder" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); Vec3 center = _args.get<Vec3 >("center",1,&_lock); Real radius = _args.get<Real >("radius",2,&_lock); Vec3 z = _args.get<Vec3 >("z",3,&_lock); obj = new Cylinder(parent,center,radius,z); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Cylinder::Cylinder" ); return 0; } catch(std::exception& e) { pbSetError("Cylinder::Cylinder",e.what()); return -1; } } + + void setRadius(Real r) { mRadius = r; } static PyObject* _W_12 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Cylinder* pbo = dynamic_cast<Cylinder*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Cylinder::setRadius"); PyObject *_retval = 0; { ArgLocker _lock; Real r = _args.get<Real >("r",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setRadius(r); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Cylinder::setRadius"); return _retval; } catch(std::exception& e) { pbSetError("Cylinder::setRadius",e.what()); return 0; } } + void setZ(Vec3 z) { mZDir=z; mZ=normalize(mZDir); } static PyObject* _W_13 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Cylinder* pbo = dynamic_cast<Cylinder*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Cylinder::setZ"); PyObject *_retval = 0; { ArgLocker _lock; Vec3 z = _args.get<Vec3 >("z",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->setZ(z); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Cylinder::setZ"); return _retval; } catch(std::exception& e) { pbSetError("Cylinder::setZ",e.what()); return 0; } } + + virtual void setCenter(const Vec3& center) { mCenter=center; } + virtual Vec3 getCenter() const { return mCenter; } + inline Real getRadius() const { return mRadius; } + inline Vec3 getZ() const { return mZ*mZDir; } + virtual Vec3 getExtent() const { return Vec3(2.0*sqrt(square(mZ)+square(mRadius))); } + virtual bool isInside(const Vec3& pos) const; + virtual void generateMesh(Mesh* mesh); + virtual void generateLevelset(Grid<Real>& phi); + +protected: + Vec3 mCenter, mZDir; Real mRadius, mZ; public: PbArgs _args;} +#define _C_Cylinder +; + + + +} //namespace +#endif + + diff --git a/source/blender/python/manta_pp/shapes.h.reg b/source/blender/python/manta_pp/shapes.h.reg new file mode 100644 index 00000000000..019a2bd40f8 --- /dev/null +++ b/source/blender/python/manta_pp/shapes.h.reg @@ -0,0 +1,25 @@ +#include "shapes.h" ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","Shape","PbClass"); template<> const char* Namify<Shape >::S = "Shape"; +>Shape^ ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","Shape",Shape::_W_0); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","applyToGrid",Shape::_W_1); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","applyToGridSmooth",Shape::_W_2); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","computeLevelset",Shape::_W_3); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","collideMesh",Shape::_W_4); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","getCenter",Shape::_W_5); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","setCenter",Shape::_W_6); ++Shape^ static const Pb::Register _R_$IDX$ ("Shape","getExtent",Shape::_W_7); ++NullShape^ static const Pb::Register _R_$IDX$ ("NullShape","NullShape","Shape"); template<> const char* Namify<NullShape >::S = "NullShape"; +>NullShape^ ++NullShape^ static const Pb::Register _R_$IDX$ ("NullShape","NullShape",NullShape::_W_8); ++Box^ static const Pb::Register _R_$IDX$ ("Box","Box","Shape"); template<> const char* Namify<Box >::S = "Box"; +>Box^ ++Box^ static const Pb::Register _R_$IDX$ ("Box","Box",Box::_W_9); ++Sphere^ static const Pb::Register _R_$IDX$ ("Sphere","Sphere","Shape"); template<> const char* Namify<Sphere >::S = "Sphere"; +>Sphere^ ++Sphere^ static const Pb::Register _R_$IDX$ ("Sphere","Sphere",Sphere::_W_10); ++Cylinder^ static const Pb::Register _R_$IDX$ ("Cylinder","Cylinder","Shape"); template<> const char* Namify<Cylinder >::S = "Cylinder"; +>Cylinder^ ++Cylinder^ static const Pb::Register _R_$IDX$ ("Cylinder","Cylinder",Cylinder::_W_11); ++Cylinder^ static const Pb::Register _R_$IDX$ ("Cylinder","setRadius",Cylinder::_W_12); ++Cylinder^ static const Pb::Register _R_$IDX$ ("Cylinder","setZ",Cylinder::_W_13); diff --git a/source/blender/python/manta_pp/shapes.h.reg.cpp b/source/blender/python/manta_pp/shapes.h.reg.cpp new file mode 100644 index 00000000000..af287c0821f --- /dev/null +++ b/source/blender/python/manta_pp/shapes.h.reg.cpp @@ -0,0 +1,43 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "shapes.h" +namespace Manta { +#ifdef _C_Box + static const Pb::Register _R_0 ("Box","Box","Shape"); template<> const char* Namify<Box >::S = "Box"; + static const Pb::Register _R_1 ("Box","Box",Box::_W_9); +#endif +#ifdef _C_Cylinder + static const Pb::Register _R_2 ("Cylinder","Cylinder","Shape"); template<> const char* Namify<Cylinder >::S = "Cylinder"; + static const Pb::Register _R_3 ("Cylinder","Cylinder",Cylinder::_W_11); + static const Pb::Register _R_4 ("Cylinder","setRadius",Cylinder::_W_12); + static const Pb::Register _R_5 ("Cylinder","setZ",Cylinder::_W_13); +#endif +#ifdef _C_NullShape + static const Pb::Register _R_6 ("NullShape","NullShape","Shape"); template<> const char* Namify<NullShape >::S = "NullShape"; + static const Pb::Register _R_7 ("NullShape","NullShape",NullShape::_W_8); +#endif +#ifdef _C_Shape + static const Pb::Register _R_8 ("Shape","Shape","PbClass"); template<> const char* Namify<Shape >::S = "Shape"; + static const Pb::Register _R_9 ("Shape","Shape",Shape::_W_0); + static const Pb::Register _R_10 ("Shape","applyToGrid",Shape::_W_1); + static const Pb::Register _R_11 ("Shape","applyToGridSmooth",Shape::_W_2); + static const Pb::Register _R_12 ("Shape","computeLevelset",Shape::_W_3); + static const Pb::Register _R_13 ("Shape","collideMesh",Shape::_W_4); + static const Pb::Register _R_14 ("Shape","getCenter",Shape::_W_5); + static const Pb::Register _R_15 ("Shape","setCenter",Shape::_W_6); + static const Pb::Register _R_16 ("Shape","getExtent",Shape::_W_7); +#endif +#ifdef _C_Sphere + static const Pb::Register _R_17 ("Sphere","Sphere","Shape"); template<> const char* Namify<Sphere >::S = "Sphere"; + static const Pb::Register _R_18 ("Sphere","Sphere",Sphere::_W_10); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/test.cpp b/source/blender/python/manta_pp/test.cpp new file mode 100644 index 00000000000..3eb5467a65f --- /dev/null +++ b/source/blender/python/manta_pp/test.cpp @@ -0,0 +1,164 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Use this file to test new functionality + * + ******************************************************************************/ + +#include "levelset.h" +#include "commonkernels.h" +#include "particle.h" +#include <cmath> + +using namespace std; + +namespace Manta { + + + +template <class S> void addToGrid(Grid<S>& a, S v) { + FOR_IDX(a) a[idx] += v; +}template <class S> static PyObject* _W_T_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "addToGrid" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<S>& a = *_args.getPtr<Grid<S> >("a",0,&_lock); S v = _args.get<S >("v",1,&_lock); _retval = getPyNone(); addToGrid(a,v); _args.check(); } pbFinalizePlugin(parent,"addToGrid" ); return _retval; } catch(std::exception& e) { pbSetError("addToGrid",e.what()); return 0; } }template <class S> static bool _K_0 (PbArgs& A) { return A.typeCheck<Grid<S> >(0,"a") && A.typeCheck<S >(1,"v"); }static PyObject* _W_0 (PyObject* s, PyObject* l, PyObject* kw) { PbArgs args(l, kw); int hits=0; PyObject* (*call)(PyObject*,PyObject*,PyObject*); if (_K_0<int>(args)) {hits++; call = _W_T_0<int>; }if (_K_0<Real>(args)) {hits++; call = _W_T_0<Real>; }if (_K_0<Vec3>(args)) {hits++; call = _W_T_0<Vec3>; } if (hits == 1) return call(s,l,kw); if (hits == 0) pbSetError("addToGrid", "Can't deduce template parameters"); else pbSetError("addToGrid", "Argument matches multiple templates"); return 0 ; } static const Pb::Register _RP_addToGrid ("","addToGrid",_W_0); + + +//! Kernel: get component (not shifted) +/*KERNEL(idx) returns(Grid<Real> ret(parent)) +Grid<Real> GetComponent2(const Grid<Vec3>& grid, int dim) { + ret[idx] = grid[idx][dim]; +}; + +PYTHON void testp(Grid<Vec3>& b) { + Grid<Real> d(b.getParent()); + b(20,20,20) = Vec3(21,22,23); + { + cout <<"middle" << endl; + Grid<Real> a = GetComponent2(b,0); + cout << a(20,20,20) << endl; + cout <<"middle" << endl; + } + cout << "end" << endl;errMsg("f"); +} +*/ + + + + struct ddtest : public KernelBase { ddtest(const Grid<Real>& v) : KernelBase(&v,0) ,v(v) ,sum(0) { run(); } inline void op(int idx, const Grid<Real>& v ,double& sum) { + sum += v[idx]; +} inline operator double () { return sum; } inline double & getRet() { return sum; } inline const Grid<Real>& getArg0() { return v; } typedef Grid<Real> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, v,sum); } const Grid<Real>& v; double sum; }; + + + + struct detest : public KernelBase { detest(const Grid<Real>& v) : KernelBase(&v,0) ,v(v) ,sum(0) { run(); } inline void op(int idx, const Grid<Real>& v ,double& sum) { + if (sum < v[idx]) + sum = v[idx]; +} inline operator double () { return sum; } inline double & getRet() { return sum; } inline const Grid<Real>& getArg0() { return v; } typedef Grid<Real> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, v,sum); } const Grid<Real>& v; double sum; }; + +void checkGrids(Grid<int>& flags1, Grid<int>& flags2, Grid<Real>& phi1, Grid<Real>& phi2, Grid<Vec3>& vel1, Grid<Vec3>& vel2) { + FOR_IJK(flags1) { + assertMsg(flags1(i,j,k) == flags2(i,j,k), "flags mismatch"); + assertMsg(norm(vel1(i,j,k)-vel2(i,j,k)) < 1e-1, "vel mismatch"); + assertMsg( fabs(phi1(i,j,k)-phi2(i,j,k)) < 1e-4, "phi mismatch"); + } +} static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "checkGrids" ); PyObject *_retval = 0; { ArgLocker _lock; Grid<int>& flags1 = *_args.getPtr<Grid<int> >("flags1",0,&_lock); Grid<int>& flags2 = *_args.getPtr<Grid<int> >("flags2",1,&_lock); Grid<Real>& phi1 = *_args.getPtr<Grid<Real> >("phi1",2,&_lock); Grid<Real>& phi2 = *_args.getPtr<Grid<Real> >("phi2",3,&_lock); Grid<Vec3>& vel1 = *_args.getPtr<Grid<Vec3> >("vel1",4,&_lock); Grid<Vec3>& vel2 = *_args.getPtr<Grid<Vec3> >("vel2",5,&_lock); _retval = getPyNone(); checkGrids(flags1,flags2,phi1,phi2,vel1,vel2); _args.check(); } pbFinalizePlugin(parent,"checkGrids" ); return _retval; } catch(std::exception& e) { pbSetError("checkGrids",e.what()); return 0; } } static const Pb::Register _RP_checkGrids ("","checkGrids",_W_1); + + +struct myvec { + myvec(int n) : x(n) { cout << "constructor" << endl; }; + myvec(const myvec& a) : x(a.x) { cout << "copy constructor" << endl; } + myvec& operator=(const myvec& a) { x=a.x; cout << "copy operator" << endl; return *this;} + int& operator[](int idx) { return x[idx]; } + + vector<int> x; +}; + + + struct testy : public KernelBase { testy(vector<int>& a) : KernelBase(a.size()) ,a(a) ,vec((size)) { run(); } inline void op(int idx, vector<int>& a ,myvec& vec) { + vec[idx] = a[idx]; +} inline operator myvec () { return vec; } inline myvec & getRet() { return vec; } inline vector<int>& getArg0() { return a; } typedef vector<int> type0; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, a,vec); } vector<int>& a; myvec vec; }; + +void kernelTest() { + cout << "kernel test" << endl; + vector<int> a(10); + for (int i=0;i<10;i++) a[i]=i; + + //testy xx(a); + myvec b = testy(a); + for (int i=0;i<10;i++) cout << b[i] << endl; + cout << "kernel end" << endl; +} static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "kernelTest" ); PyObject *_retval = 0; { ArgLocker _lock; _retval = getPyNone(); kernelTest(); _args.check(); } pbFinalizePlugin(parent,"kernelTest" ); return _retval; } catch(std::exception& e) { pbSetError("kernelTest",e.what()); return 0; } } static const Pb::Register _RP_kernelTest ("","kernelTest",_W_2); + +void getCurl(MACGrid& vel, Grid<Real>& vort, int comp) { + Grid<Vec3> velCenter(vel.getParent()), curl(vel.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GetComponent(curl, vort, comp); +} static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "getCurl" ); PyObject *_retval = 0; { ArgLocker _lock; MACGrid& vel = *_args.getPtr<MACGrid >("vel",0,&_lock); Grid<Real>& vort = *_args.getPtr<Grid<Real> >("vort",1,&_lock); int comp = _args.get<int >("comp",2,&_lock); _retval = getPyNone(); getCurl(vel,vort,comp); _args.check(); } pbFinalizePlugin(parent,"getCurl" ); return _retval; } catch(std::exception& e) { pbSetError("getCurl",e.what()); return 0; } } static const Pb::Register _RP_getCurl ("","getCurl",_W_3); + +void setinflow(FlagGrid& flags, MACGrid& vel, LevelsetGrid& phi, Real h) { + FOR_IJK(vel) { + if (i<=2) { + if (j < h*flags.getSizeY()) { + vel(i,j,k).x = 1; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 1; + phi(i,j,k) = -1; + } + } else { + vel(i,j,k).x = 0; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 4; + phi(i,j,k) = 1; + } + } + } + else if (i>=flags.getSizeX()-2) { + vel(i,j,k).x = 1; + /*if (j < 30-12) { + vel(i,j,k).x = 1; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 1; + phi(i,j,k) = -1; + } + } else { + vel(i,j,k).x = 0; + if (!flags.isObstacle(i,j,k)) { + flags(i,j,k) = 4; + phi(i,j,k) = 1; + } + }*/ + } + } +} static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "setinflow" ); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); MACGrid& vel = *_args.getPtr<MACGrid >("vel",1,&_lock); LevelsetGrid& phi = *_args.getPtr<LevelsetGrid >("phi",2,&_lock); Real h = _args.get<Real >("h",3,&_lock); _retval = getPyNone(); setinflow(flags,vel,phi,h); _args.check(); } pbFinalizePlugin(parent,"setinflow" ); return _retval; } catch(std::exception& e) { pbSetError("setinflow",e.what()); return 0; } } static const Pb::Register _RP_setinflow ("","setinflow",_W_4); + +void testDiscardNth(BasicParticleSystem& parts, int skip=1) { + //knSetPdataConst<Real>(pd,value); + for(int i=0; i<parts.size(); ++i) { + if(i%(skip+1) == skip) { // keep + } else { + parts.setPos(i, Vec3(-100000) ); + } + } +} static PyObject* _W_5 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); pbPreparePlugin(parent, "testDiscardNth" ); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem& parts = *_args.getPtr<BasicParticleSystem >("parts",0,&_lock); int skip = _args.getOpt<int >("skip",1,1,&_lock); _retval = getPyNone(); testDiscardNth(parts,skip); _args.check(); } pbFinalizePlugin(parent,"testDiscardNth" ); return _retval; } catch(std::exception& e) { pbSetError("testDiscardNth",e.what()); return 0; } } static const Pb::Register _RP_testDiscardNth ("","testDiscardNth",_W_5); + +} //namespace + + + diff --git a/source/blender/python/manta_pp/timing.cpp b/source/blender/python/manta_pp/timing.cpp new file mode 100644 index 00000000000..e421d7e689f --- /dev/null +++ b/source/blender/python/manta_pp/timing.cpp @@ -0,0 +1,128 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugin timing + * + ******************************************************************************/ + +#include "timing.h" +#include <fstream> + +using namespace std; +namespace Manta { + +TimingData::TimingData() : updated(false), num(0) { +} + +void TimingData::start(FluidSolver* parent, const string& name) { + mLastPlugin = name; + mPluginTimer.get(); +} + +void TimingData::stop(FluidSolver* parent, const string& name) { + if (mLastPlugin == name && name != "FluidSolver::step") { + updated = true; + const string parentName = parent ? parent->getName() : ""; + MuTime diff = mPluginTimer.update(); + vector<TimingSet>& cur = mData[name]; + for (vector<TimingSet>::iterator it = cur.begin(); it != cur.end(); it++) { + if (it->solver == parentName) { + it->cur += diff; + it->updated = true; + return; + } + } + TimingSet s; + s.solver = parentName; + s.cur = diff; + s.updated = true; + cur.push_back(s); + } +} + +void TimingData::step() { + if (updated) + num++; + std::map<std::string, std::vector<TimingSet> >::iterator it; + for (it = mData.begin(); it != mData.end(); it++) { + for (vector<TimingSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) { + if (it2->updated) { + it2->total += it2->cur; + it2->num++; + } + it2->cur.clear(); + it2->updated = false; + } + } + updated = false; +} + +void TimingData::print() { + MuTime total; + total.clear(); + std::map<std::string, std::vector<TimingSet> >::iterator it; + for (it = mData.begin(); it != mData.end(); it++) + for (vector<TimingSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) + total += it2->cur; + + printf("\n-- STEP %3d ----------------------------\n", num); + for (it = mData.begin(); it != mData.end(); it++) { + for (vector<TimingSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) { + if (!it2->updated) continue; + string name = it->first; + if (it->second.size() > 1 && !it2->solver.empty()) + name += "[" + it2->solver + "]"; + printf("[%4.1f%%] %s (%s)\n", 100.0*((Real)it2->cur.time / (Real)total.time), + name.c_str(), it2->cur.toString().c_str()); + } + } + step(); + + printf("----------------------------------------\n"); + printf("Total : %s\n\n", total.toString().c_str()); +} + +void TimingData::saveMean(const string& filename) { + ofstream ofs(filename.c_str()); + step(); + if (!ofs.good()) + errMsg("can't open " + filename + " as timing log"); + ofs << "Mean timings of " << num << " steps :" <<endl <<endl; + MuTime total; + total.clear(); + std::map<std::string, std::vector<TimingSet> >::iterator it; + for (it = mData.begin(); it != mData.end(); it++) + for (vector<TimingSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) { + total += it2->cur; + string name = it->first; + if (it->second.size() > 1) + name += "[" + it2->solver + "]"; + + ofs << name << " " << (it2->total / it2->num) << endl; + } + + ofs << endl << "Total : " << total << " (mean " << total/num << ")" << endl; + ofs.close(); +} + +} + + + diff --git a/source/blender/python/manta_pp/timing.h b/source/blender/python/manta_pp/timing.h new file mode 100644 index 00000000000..0aa0714523a --- /dev/null +++ b/source/blender/python/manta_pp/timing.h @@ -0,0 +1,72 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Plugin timing + * + ******************************************************************************/ + +#ifndef _TIMING_H +#define _TIMING_H + +#include "manta.h" +#include <map> +namespace Manta { + + +class TimingData { +private: + TimingData(); +public: + static TimingData& instance() { static TimingData a; return a; } + + void print(); + void saveMean(const std::string& filename); + void start(FluidSolver* parent, const std::string& name); + void stop(FluidSolver* parent, const std::string& name); +protected: + void step(); + struct TimingSet { + TimingSet() : num(0),updated(false) { cur.clear(); total.clear(); } + MuTime cur, total; + int num; + bool updated; + std::string solver; + }; + bool updated; + + int num; + MuTime mPluginTimer; + std::string mLastPlugin; + std::map<std::string, std::vector<TimingSet> > mData; +}; + +// Python interface +class Timings : public PbClass {public: + Timings() :PbClass(0){} static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "Timings::Timings" ); { ArgLocker _lock; obj = new Timings(); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"Timings::Timings" ); return 0; } catch(std::exception& e) { pbSetError("Timings::Timings",e.what()); return -1; } } + + void display() { TimingData::instance().print(); } static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Timings* pbo = dynamic_cast<Timings*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Timings::display"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->display(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Timings::display"); return _retval; } catch(std::exception& e) { pbSetError("Timings::display",e.what()); return 0; } } void saveMean(std::string file) { TimingData::instance().saveMean(file); } static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); Timings* pbo = dynamic_cast<Timings*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "Timings::saveMean"); PyObject *_retval = 0; { ArgLocker _lock; std::string file = _args.get<std::string >("file",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->saveMean(file); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"Timings::saveMean"); return _retval; } catch(std::exception& e) { pbSetError("Timings::saveMean",e.what()); return 0; } } public: PbArgs _args;} +#define _C_Timings +; + +} + +#endif + + diff --git a/source/blender/python/manta_pp/timing.h.reg b/source/blender/python/manta_pp/timing.h.reg new file mode 100644 index 00000000000..4a653bf381d --- /dev/null +++ b/source/blender/python/manta_pp/timing.h.reg @@ -0,0 +1,6 @@ +#include "timing.h" ++Timings^ static const Pb::Register _R_$IDX$ ("Timings","Timings","PbClass"); template<> const char* Namify<Timings >::S = "Timings"; +>Timings^ ++Timings^ static const Pb::Register _R_$IDX$ ("Timings","Timings",Timings::_W_0); ++Timings^ static const Pb::Register _R_$IDX$ ("Timings","display",Timings::_W_1); ++Timings^ static const Pb::Register _R_$IDX$ ("Timings","saveMean",Timings::_W_2); diff --git a/source/blender/python/manta_pp/timing.h.reg.cpp b/source/blender/python/manta_pp/timing.h.reg.cpp new file mode 100644 index 00000000000..f4151a3e9cd --- /dev/null +++ b/source/blender/python/manta_pp/timing.h.reg.cpp @@ -0,0 +1,20 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "timing.h" +namespace Manta { +#ifdef _C_Timings + static const Pb::Register _R_0 ("Timings","Timings","PbClass"); template<> const char* Namify<Timings >::S = "Timings"; + static const Pb::Register _R_1 ("Timings","Timings",Timings::_W_0); + static const Pb::Register _R_2 ("Timings","display",Timings::_W_1); + static const Pb::Register _R_3 ("Timings","saveMean",Timings::_W_2); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/turbulencepart.cpp b/source/blender/python/manta_pp/turbulencepart.cpp new file mode 100644 index 00000000000..796c1417fb1 --- /dev/null +++ b/source/blender/python/manta_pp/turbulencepart.cpp @@ -0,0 +1,154 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.ynu.org/licenses + * + * Turbulence particles + * + ******************************************************************************/ + +#include "turbulencepart.h" +#include "shapes.h" +#include "randomstream.h" + +using namespace std; +namespace Manta { + +TurbulenceParticleSystem::TurbulenceParticleSystem(FluidSolver* parent, WaveletNoiseField& noise) : + ParticleSystem<TurbulenceParticleData>(parent), noise(noise) +{ +} + +ParticleBase* TurbulenceParticleSystem::clone() { + TurbulenceParticleSystem* nm = new TurbulenceParticleSystem(getParent(), noise); + compress(); + + nm->mData = mData; + nm->setName(getName()); + return nm; +} + +inline Vec3 hsv2rgb(Real h, Real s, Real v){ + Real r=0, g=0, b=0; + + int i = (int)(h * 6); + Real f = h * 6 - i; + Real p = v * (1 - s); + Real q = v * (1 - f * s); + Real t = v * (1 - (1 - f) * s); + + switch(i % 6){ + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + default: break; + } + + return Vec3(r,g,b); +} + +void TurbulenceParticleSystem::seed(Shape* shape, int num) { + static RandomStream rand(34894231); + Vec3 sz = shape->getExtent(), p0 = shape->getCenter() - sz*0.5; + for (int i=0; i<num; i++) { + Vec3 p; + do { + p = rand.getVec3() * sz + p0; + } while(!shape->isInside(p)); + Real z = (p.z - p0.z)/sz.z; + add(TurbulenceParticleData(p,hsv2rgb(z,0.75,1.0))); + } +} + +void TurbulenceParticleSystem::resetTexCoords(int num, const Vec3& inflow ) { + if (num==0) { + for (int i=0; i<size(); i++) mData[i].tex0 = mData[i].pos - inflow; + } else { + for (int i=0; i<size(); i++) mData[i].tex1 = mData[i].pos - inflow; + } +} + + + + + struct KnSynthesizeTurbulence : public KernelBase { KnSynthesizeTurbulence(TurbulenceParticleSystem& p, FlagGrid& flags, WaveletNoiseField& noise, Grid<Real>& kGrid, Real alpha, Real dt, int octaves, Real scale, Real invL0, Real kmin) : KernelBase(p.size()) ,p(p),flags(flags),noise(noise),kGrid(kGrid),alpha(alpha),dt(dt),octaves(octaves),scale(scale),invL0(invL0),kmin(kmin) { run(); } inline void op(int idx, TurbulenceParticleSystem& p, FlagGrid& flags, WaveletNoiseField& noise, Grid<Real>& kGrid, Real alpha, Real dt, int octaves, Real scale, Real invL0, Real kmin ) { + const Real PERSISTENCE = 0.56123f; + + const Vec3 pos(p[idx].pos); + if (flags.isInBounds(pos)) { // && !flags.isObstacle(pos)) { + Real k2 = kGrid.getInterpolated(pos)-kmin; + Real ks = k2<0 ? 0.0 : sqrt(k2); + + // Wavelet noise lookup + Real amplitude = scale * ks; + Real multiplier = invL0; + Vec3 vel(0.); + for (int o=0; o<octaves; o++) { + //Vec3 ns = noise.evaluateCurl(p[i].pos * multiplier) * amplitude; + Vec3 n0 = noise.evaluateCurl(p[idx].tex0 * multiplier) * amplitude; + Vec3 n1 = noise.evaluateCurl(p[idx].tex1 * multiplier) * amplitude; + vel += alpha * n0 + (1.0f-alpha) * n1; + + // next scale + amplitude *= PERSISTENCE; + multiplier *= 2.0f; + } + + // advection + Vec3 dx = vel*dt; + p[idx].pos += dx; + p[idx].tex0 += dx; + p[idx].tex1 += dx; + } +} inline TurbulenceParticleSystem& getArg0() { return p; } typedef TurbulenceParticleSystem type0;inline FlagGrid& getArg1() { return flags; } typedef FlagGrid type1;inline WaveletNoiseField& getArg2() { return noise; } typedef WaveletNoiseField type2;inline Grid<Real>& getArg3() { return kGrid; } typedef Grid<Real> type3;inline Real& getArg4() { return alpha; } typedef Real type4;inline Real& getArg5() { return dt; } typedef Real type5;inline int& getArg6() { return octaves; } typedef int type6;inline Real& getArg7() { return scale; } typedef Real type7;inline Real& getArg8() { return invL0; } typedef Real type8;inline Real& getArg9() { return kmin; } typedef Real type9; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, p,flags,noise,kGrid,alpha,dt,octaves,scale,invL0,kmin); } TurbulenceParticleSystem& p; FlagGrid& flags; WaveletNoiseField& noise; Grid<Real>& kGrid; Real alpha; Real dt; int octaves; Real scale; Real invL0; Real kmin; }; + +void TurbulenceParticleSystem::synthesize(FlagGrid& flags, Grid<Real>& k, int octaves, Real switchLength, Real L0, Real scale, Vec3 inflowBias) { + static Real ctime = 0; + static Vec3 inflow(0.); + Real dt = getParent()->getDt(); + + // collect inflow bias + inflow += inflowBias * dt; + + // alpha: hat function over time + Real oldAlpha = 2.0f*nmod(ctime/switchLength, Real(1.0) ); + ctime += dt; + Real alpha = 2.0f*nmod(ctime/switchLength, Real(1.0) ); + + if (oldAlpha < 1.0f && alpha >= 1.0f) resetTexCoords(0, inflow); + if (oldAlpha > alpha) resetTexCoords(1, inflow); + if (alpha>1.0f) alpha=2.0f-alpha; + alpha = 1.0; + + KnSynthesizeTurbulence(*this, flags, noise, k, alpha, dt, octaves, scale, 1.0f/L0, 1.5*square(0.1)); +} + +void TurbulenceParticleSystem::deleteInObstacle(FlagGrid& flags) { + for (int i=0; i<size(); i++) + if (flags.isObstacle(mData[i].pos)) + mData[i].flag |= PDELETE; + compress(); +} + + +} // namespace + + diff --git a/source/blender/python/manta_pp/turbulencepart.h b/source/blender/python/manta_pp/turbulencepart.h new file mode 100644 index 00000000000..4ffb5f89446 --- /dev/null +++ b/source/blender/python/manta_pp/turbulencepart.h @@ -0,0 +1,64 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Turbulence particles + * + ******************************************************************************/ + +#ifndef _TURBULENCEPART_H_ +#define _TURBULENCEPART_H_ + +#include "particle.h" +#include "noisefield.h" + +namespace Manta { +class Shape; + + + +struct TurbulenceParticleData { + TurbulenceParticleData() : pos(_0),color(1.),tex0(_0),tex1(_0),flag(0) {} + TurbulenceParticleData(const Vec3& p, const Vec3& color = Vec3(1.)) : pos(p),color(color),tex0(p),tex1(p),flag(0) {} + Vec3 pos, color; + Vec3 tex0, tex1; + int flag; + static ParticleBase::SystemType getType() { return ParticleBase::TURBULENCE; } +}; + +//! Turbulence particles +class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> {public: + TurbulenceParticleSystem(FluidSolver* parent, WaveletNoiseField& noise); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "TurbulenceParticleSystem::TurbulenceParticleSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); WaveletNoiseField& noise = *_args.getPtr<WaveletNoiseField >("noise",1,&_lock); obj = new TurbulenceParticleSystem(parent,noise); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"TurbulenceParticleSystem::TurbulenceParticleSystem" ); return 0; } catch(std::exception& e) { pbSetError("TurbulenceParticleSystem::TurbulenceParticleSystem",e.what()); return -1; } } + + void resetTexCoords(int num, const Vec3& inflow); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); TurbulenceParticleSystem* pbo = dynamic_cast<TurbulenceParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "TurbulenceParticleSystem::resetTexCoords"); PyObject *_retval = 0; { ArgLocker _lock; int num = _args.get<int >("num",0,&_lock); const Vec3& inflow = _args.get<Vec3 >("inflow",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->resetTexCoords(num,inflow); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"TurbulenceParticleSystem::resetTexCoords"); return _retval; } catch(std::exception& e) { pbSetError("TurbulenceParticleSystem::resetTexCoords",e.what()); return 0; } } + void seed(Shape* source, int num); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); TurbulenceParticleSystem* pbo = dynamic_cast<TurbulenceParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "TurbulenceParticleSystem::seed"); PyObject *_retval = 0; { ArgLocker _lock; Shape* source = _args.getPtr<Shape >("source",0,&_lock); int num = _args.get<int >("num",1,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->seed(source,num); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"TurbulenceParticleSystem::seed"); return _retval; } catch(std::exception& e) { pbSetError("TurbulenceParticleSystem::seed",e.what()); return 0; } } + void synthesize(FlagGrid& flags, Grid<Real>& k, int octaves=2, Real switchLength=10.0, Real L0=0.1, Real scale=1.0, Vec3 inflowBias=_0); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); TurbulenceParticleSystem* pbo = dynamic_cast<TurbulenceParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "TurbulenceParticleSystem::synthesize"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); Grid<Real>& k = *_args.getPtr<Grid<Real> >("k",1,&_lock); int octaves = _args.getOpt<int >("octaves",2,2,&_lock); Real switchLength = _args.getOpt<Real >("switchLength",3,10.0,&_lock); Real L0 = _args.getOpt<Real >("L0",4,0.1,&_lock); Real scale = _args.getOpt<Real >("scale",5,1.0,&_lock); Vec3 inflowBias = _args.getOpt<Vec3 >("inflowBias",6,_0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->synthesize(flags,k,octaves,switchLength,L0,scale,inflowBias); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"TurbulenceParticleSystem::synthesize"); return _retval; } catch(std::exception& e) { pbSetError("TurbulenceParticleSystem::synthesize",e.what()); return 0; } } + void deleteInObstacle(FlagGrid& flags); static PyObject* _W_4 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); TurbulenceParticleSystem* pbo = dynamic_cast<TurbulenceParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "TurbulenceParticleSystem::deleteInObstacle"); PyObject *_retval = 0; { ArgLocker _lock; FlagGrid& flags = *_args.getPtr<FlagGrid >("flags",0,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->deleteInObstacle(flags); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"TurbulenceParticleSystem::deleteInObstacle"); return _retval; } catch(std::exception& e) { pbSetError("TurbulenceParticleSystem::deleteInObstacle",e.what()); return 0; } } + + virtual ParticleBase* clone(); + +private: WaveletNoiseField& noise; public: PbArgs _args;} +#define _C_TurbulenceParticleSystem +; + +} // namespace + + +#endif + diff --git a/source/blender/python/manta_pp/turbulencepart.h.reg b/source/blender/python/manta_pp/turbulencepart.h.reg new file mode 100644 index 00000000000..f28cd70f4a3 --- /dev/null +++ b/source/blender/python/manta_pp/turbulencepart.h.reg @@ -0,0 +1,9 @@ +#include "turbulencepart.h" ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","TurbulenceParticleSystem","ParticleSystem<$BT$>"); template<> const char* Namify<TurbulenceParticleSystem >::S = "TurbulenceParticleSystem"; +>TurbulenceParticleSystem^ +@TurbulenceParticleSystem^^ParticleSystem^TurbulenceParticleData ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","TurbulenceParticleSystem",TurbulenceParticleSystem::_W_0); ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","resetTexCoords",TurbulenceParticleSystem::_W_1); ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","seed",TurbulenceParticleSystem::_W_2); ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","synthesize",TurbulenceParticleSystem::_W_3); ++TurbulenceParticleSystem^ static const Pb::Register _R_$IDX$ ("TurbulenceParticleSystem","deleteInObstacle",TurbulenceParticleSystem::_W_4); diff --git a/source/blender/python/manta_pp/turbulencepart.h.reg.cpp b/source/blender/python/manta_pp/turbulencepart.h.reg.cpp new file mode 100644 index 00000000000..04a1a422ea8 --- /dev/null +++ b/source/blender/python/manta_pp/turbulencepart.h.reg.cpp @@ -0,0 +1,34 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "turbulencepart.h" +namespace Manta { +#ifdef _C_ParticleSystem + static const Pb::Register _R_0 ("ParticleSystem<TurbulenceParticleData>","ParticleSystem<TurbulenceParticleData>","ParticleBase"); template<> const char* Namify<ParticleSystem<TurbulenceParticleData> >::S = "ParticleSystem<TurbulenceParticleData>"; + static const Pb::Register _R_1 ("ParticleSystem<TurbulenceParticleData>","ParticleSystem",ParticleSystem<TurbulenceParticleData>::_W_2); + static const Pb::Register _R_2 ("ParticleSystem<TurbulenceParticleData>","size",ParticleSystem<TurbulenceParticleData>::_W_3); + static const Pb::Register _R_3 ("ParticleSystem<TurbulenceParticleData>","setPos",ParticleSystem<TurbulenceParticleData>::_W_4); + static const Pb::Register _R_4 ("ParticleSystem<TurbulenceParticleData>","getPos",ParticleSystem<TurbulenceParticleData>::_W_5); + static const Pb::Register _R_5 ("ParticleSystem<TurbulenceParticleData>","getPosPdata",ParticleSystem<TurbulenceParticleData>::_W_6); + static const Pb::Register _R_6 ("ParticleSystem<TurbulenceParticleData>","setPosPdata",ParticleSystem<TurbulenceParticleData>::_W_7); + static const Pb::Register _R_7 ("ParticleSystem<TurbulenceParticleData>","clear",ParticleSystem<TurbulenceParticleData>::_W_8); + static const Pb::Register _R_8 ("ParticleSystem<TurbulenceParticleData>","advectInGrid",ParticleSystem<TurbulenceParticleData>::_W_9); + static const Pb::Register _R_9 ("ParticleSystem<TurbulenceParticleData>","projectOutside",ParticleSystem<TurbulenceParticleData>::_W_10); +#endif +#ifdef _C_TurbulenceParticleSystem + static const Pb::Register _R_10 ("TurbulenceParticleSystem","TurbulenceParticleSystem","ParticleSystem<TurbulenceParticleData>"); template<> const char* Namify<TurbulenceParticleSystem >::S = "TurbulenceParticleSystem"; + static const Pb::Register _R_11 ("TurbulenceParticleSystem","TurbulenceParticleSystem",TurbulenceParticleSystem::_W_0); + static const Pb::Register _R_12 ("TurbulenceParticleSystem","resetTexCoords",TurbulenceParticleSystem::_W_1); + static const Pb::Register _R_13 ("TurbulenceParticleSystem","seed",TurbulenceParticleSystem::_W_2); + static const Pb::Register _R_14 ("TurbulenceParticleSystem","synthesize",TurbulenceParticleSystem::_W_3); + static const Pb::Register _R_15 ("TurbulenceParticleSystem","deleteInObstacle",TurbulenceParticleSystem::_W_4); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/util/integrator.h b/source/blender/python/manta_pp/util/integrator.h new file mode 100644 index 00000000000..378a890c0ab --- /dev/null +++ b/source/blender/python/manta_pp/util/integrator.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for simple integration + * + ******************************************************************************/ + +#ifndef _INTEGRATE_H +#define _INTEGRATE_H + +#include <vector> +#include "vectorbase.h" +#include "kernel.h" + +namespace Manta { + +enum IntegrationMode { IntEuler=0, IntRK2, IntRK4 }; + +//! Integrate a particle set with a given velocity kernel +template<class VelKernel> +void integratePointSet(VelKernel& k, int mode) { + typedef typename VelKernel::type0 PosType; + PosType& x = k.getArg0(); + const std::vector<Vec3>& u = k.getRet(); + const int N = x.size(); + + if (mode == IntEuler) { + for(int i=0; i<N; i++) x[i].pos += u[i]; + } + else if (mode == IntRK2) { + PosType x0(x); + + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + 0.5*u[i]; + + k.run(); + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + u[i]; + } + else if (mode == IntRK4) { + PosType x0(x); + std::vector<Vec3> uTotal(u); + + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + 0.5*u[i]; + + k.run(); + for(int i=0; i<N; i++) { + x[i].pos = x0[i].pos + 0.5*u[i]; + uTotal[i] += 2*u[i]; + } + + k.run(); + for(int i=0; i<N; i++) { + x[i].pos = x0[i].pos + u[i]; + uTotal[i] += 2*u[i]; + } + + k.run(); + for(int i=0; i<N; i++) x[i].pos = x0[i].pos + (_1/6) * (uTotal[i] + u[i]); + } + else + errMsg("unknown integration type"); + + //for(int i=0; i<N; i++) std::cout << x[i].pos.y-x[0].pos.y << std::endl; + //std::cout << "<><><>" << std::endl; +} + + +} // namespace + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/util/interpol.h b/source/blender/python/manta_pp/util/interpol.h new file mode 100644 index 00000000000..073df798ffa --- /dev/null +++ b/source/blender/python/manta_pp/util/interpol.h @@ -0,0 +1,209 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for interpolation + * + ******************************************************************************/ + +#ifndef _INTERPOL_H +#define _INTERPOL_H + +#include "vectorbase.h" + +// Grid values are stored at i+0.5, j+0.5, k+0.5 +// MAC grid values are stored at i,j+0.5,k+0.5 (for x) ... + +namespace Manta { + +inline Vec3 fdTangent(const Vec3& p0, const Vec3& p1, const Vec3& p2) { + return 0.5*(getNormalized(p2-p1) + getNormalized(p1-p0)); +} + +inline Vec3 crTangent(const Vec3& p0, const Vec3& p1, const Vec3& p2) { + return 0.5*(p2-p0); +} + +inline Vec3 hermiteSpline(const Vec3& p0, const Vec3& p1, const Vec3& m0, const Vec3& m1, Real t) { + const Real t2=t*t, t3=t2*t; + return (2.0*t3 - 3.0*t2 + 1.0)*p0 + (t3 - 2.0*t2 + t)*m0 + (-2.0*t3 + 3.0*t2)*p1 + (t3 - t2)*m1; +} + +static inline void checkIndexInterpol(const Vec3i& size, int idx) { + if (idx<0 || idx > size.x * size.y * size.z) { + std::ostringstream s; + s << "Grid interpol dim " << size << " : index " << idx << " out of bound "; + errMsg(s.str()); + } +} + + +// ---------------------------------------------------------------------- +// Grid interpolators +// ---------------------------------------------------------------------- + +#define BUILD_INDEX \ + Real px=pos.x-0.5f, py=pos.y-0.5f, pz=pos.z-0.5f; \ + int xi = (int)px; \ + int yi = (int)py; \ + int zi = (int)pz; \ + Real s1 = px-(Real)xi, s0 = 1.-s1; \ + Real t1 = py-(Real)yi, t0 = 1.-t1; \ + Real f1 = pz-(Real)zi, f0 = 1.-f1; \ + /* clamp to border */ \ + if (px < 0.) { xi = 0; s0 = 1.0; s1 = 0.0; } \ + if (py < 0.) { yi = 0; t0 = 1.0; t1 = 0.0; } \ + if (pz < 0.) { zi = 0; f0 = 1.0; f1 = 0.0; } \ + if (xi >= size.x-1) { xi = size.x-2; s0 = 0.0; s1 = 1.0; } \ + if (yi >= size.y-1) { yi = size.y-2; t0 = 0.0; t1 = 1.0; } \ + if (size.z>1) { if (zi >= size.z-1) { zi = size.z-2; f0 = 0.0; f1 = 1.0; } } \ + const int X = 1; \ + const int Y = size.x; + +template <class T> +inline T interpol(const T* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx] *t0 + data[idx+Y] *t1) * s0 + + (data[idx+X]*t0 + data[idx+X+Y]*t1) * s1) * f0 + +((data[idx+Z]*t0 + data[idx+Y+Z]*t1) * s0 + + (data[idx+X+Z]*t0 + data[idx+X+Y+Z]*t1) * s1) * f1; +} + +template <int c> +inline Real interpolComponent(const Vec3* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx][c] *t0 + data[idx+Y][c] *t1) * s0 + + (data[idx+X][c]*t0 + data[idx+X+Y][c]*t1) * s1) * f0 + +((data[idx+Z][c]*t0 + data[idx+Y+Z][c]*t1) * s0 + + (data[idx+X+Z][c]*t0 + data[idx+X+Y+Z][c]*t1) * s1) * f1; +} + +template<class T> +inline void setInterpol(T* data, const Vec3i& size, const int Z, const Vec3& pos, const T& v, Real* sumBuffer) +{ + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + T* ref = &data[idx]; + Real* sum = &sumBuffer[idx]; + Real s0f0=s0*f0, s1f0=s1*f0, s0f1=s0*f1, s1f1=s1*f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[Z] += wz; sum[X+Z] += wxz; sum[Y+Z] += wyz; sum[X+Y+Z] += wxyz; + ref[Z] += wz*v; ref[X+Z] += wxz*v; ref[Y+Z] += wyz*v; ref[X+Y+Z] += wxyz*v; + sum[0] += w0; sum[X] += wx; sum[Y] += wy; sum[X+Y] += wxy; + ref[0] += w0*v; ref[X] += wx*v; ref[Y] += wy*v; ref[X+Y] += wxy*v; +} + + +#define BUILD_INDEX_SHIFT \ + BUILD_INDEX \ + /* shifted coords */ \ + int s_xi = (int)pos.x, s_yi = (int)pos.y, s_zi = (int)pos.z; \ + Real s_s1 = pos.x-(Real)s_xi, s_s0 = 1.-s_s1; \ + Real s_t1 = pos.y-(Real)s_yi, s_t0 = 1.-s_t1; \ + Real s_f1 = pos.z-(Real)s_zi, s_f0 = 1.-s_f1; \ + /* clamp to border */ \ + if (pos.x < 0) { s_xi = 0; s_s0 = 1.0; s_s1 = 0.0; } \ + if (pos.y < 0) { s_yi = 0; s_t0 = 1.0; s_t1 = 0.0; } \ + if (pos.z < 0) { s_zi = 0; s_f0 = 1.0; s_f1 = 0.0; } \ + if (s_xi >= size.x-1) { s_xi = size.x-2; s_s0 = 0.0; s_s1 = 1.0; } \ + if (s_yi >= size.y-1) { s_yi = size.y-2; s_t0 = 0.0; s_t1 = 1.0; } \ + if (size.z>1) { if (s_zi >= size.z-1) { s_zi = size.z-2; s_f0 = 0.0; s_f1 = 1.0; } } + +inline Vec3 interpolMAC(const Vec3* data, const Vec3i& size, const int Z, const Vec3& pos) +{ + BUILD_INDEX_SHIFT + DEBUG_ONLY(checkIndexInterpol(size,(zi*size.y+yi)*size.x+xi)); + DEBUG_ONLY(checkIndexInterpol(size,(s_zi*size.y+s_yi)*size.x+s_xi+X+Y+Z)); + + // process individual components + Vec3 ret(0.); + { // X + const Vec3* ref = &data[((zi*size.y+yi)*size.x+s_xi)]; + ret.x = f0 * ((ref[0].x*t0 + ref[Y].x*t1 )*s_s0 + + (ref[X].x*t0 + ref[X+Y].x*t1 )*s_s1) + + f1 * ((ref[Z].x*t0 + ref[Z+Y].x*t1 )*s_s0 + + (ref[X+Z].x*t0 + ref[X+Y+Z].x*t1 )*s_s1 ); + } + { // Y + const Vec3* ref = &data[((zi*size.y+s_yi)*size.x+xi)]; + ret.y = f0 * ((ref[0].y*s_t0 + ref[Y].y*s_t1 )*s0 + + (ref[X].y*s_t0 + ref[X+Y].y*s_t1 )*s1) + + f1 * ((ref[Z].y*s_t0 + ref[Z+Y].y*s_t1 )*s0 + + (ref[X+Z].y*s_t0 + ref[X+Y+Z].y*s_t1 )*s1 ); + } + { // Z + const Vec3* ref = &data[((s_zi*size.y+yi)*size.x+xi)]; + ret.z = s_f0 * ((ref[0].z*t0 + ref[Y].z*t1 )*s0 + + (ref[X].z*t0 + ref[X+Y].z*t1 )*s1) + + s_f1 * ((ref[Z].z*t0 + ref[Z+Y].z*t1 )*s0 + + (ref[X+Z].z*t0 + ref[X+Y+Z].z*t1 )*s1 ); + } + return ret; +} + +inline void setInterpolMAC(Vec3* data, const Vec3i& size, const int Z, const Vec3& pos, const Vec3& val, Vec3* sumBuffer) +{ + BUILD_INDEX_SHIFT + DEBUG_ONLY(checkIndexInterpol(size,(zi*size.y+yi)*size.x+xi)); + DEBUG_ONLY(checkIndexInterpol(size,(s_zi*size.y+s_yi)*size.x+s_xi+X+Y+Z)); + + // process individual components + { // X + const int idx = (zi*size.y+yi)*size.x+s_xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s_s0*f0, s1f0=s_s1*f0, s0f1=s_s0*f1, s1f1=s_s1*f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[Z].x += wz; sum[X+Z].x += wxz; sum[Y+Z].x += wyz; sum[X+Y+Z].x += wxyz; + ref[Z].x += wz*val.x; ref[X+Z].x += wxz*val.x; ref[Y+Z].x += wyz*val.x; ref[X+Y+Z].x += wxyz*val.x; + sum[0].x += w0; sum[X].x += wx; sum[Y].x += wy; sum[X+Y].x += wxy; + ref[0].x += w0*val.x; ref[X].x += wx*val.x; ref[Y].x += wy*val.x; ref[X+Y].x += wxy*val.x; + } + { // Y + const int idx = (zi*size.y+s_yi)*size.x+xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s0*f0, s1f0=s1*f0, s0f1=s0*f1, s1f1=s1*f1; + Real w0 = s_t0*s0f0, wx = s_t0*s1f0, wy = s_t1*s0f0, wxy = s_t1*s1f0; + Real wz = s_t0*s0f1, wxz = s_t0*s1f1, wyz = s_t1*s0f1, wxyz = s_t1*s1f1; + + sum[Z].y += wz; sum[X+Z].y += wxz; sum[Y+Z].y += wyz; sum[X+Y+Z].y += wxyz; + ref[Z].y += wz*val.y; ref[X+Z].y += wxz*val.y; ref[Y+Z].y += wyz*val.y; ref[X+Y+Z].y += wxyz*val.y; + sum[0].y += w0; sum[X].y += wx; sum[Y].y += wy; sum[X+Y].y += wxy; + ref[0].y += w0*val.y; ref[X].y += wx*val.y; ref[Y].y += wy*val.y; ref[X+Y].y += wxy*val.y; + } + { // Z + const int idx = (s_zi*size.y+yi)*size.x+xi; + Vec3 *ref = &data[idx], *sum = &sumBuffer[idx]; + Real s0f0=s0*s_f0, s1f0=s1*s_f0, s0f1=s0*s_f1, s1f1=s1*s_f1; + Real w0 = t0*s0f0, wx = t0*s1f0, wy = t1*s0f0, wxy = t1*s1f0; + Real wz = t0*s0f1, wxz = t0*s1f1, wyz = t1*s0f1, wxyz = t1*s1f1; + + sum[0].z += w0; sum[X].z += wx; sum[Y].z += wy; sum[X+Y].z += wxy; + sum[Z].z += wz; sum[X+Z].z += wxz; sum[Y+Z].z += wyz; sum[X+Y+Z].z += wxyz; + ref[0].z += w0*val.z; ref[X].z += wx*val.z; ref[Y].z += wy*val.z; ref[X+Y].z += wxy*val.z; + ref[Z].z += wz*val.z; ref[X+Z].z += wxz*val.z; ref[Y+Z].z += wyz*val.z; ref[X+Y+Z].z += wxyz*val.z; + } +} + +#undef BUILD_INDEX +#undef BUILD_INDEX_SHIFT + +} //namespace + +#endif diff --git a/source/blender/python/manta_pp/util/interpolHigh.h b/source/blender/python/manta_pp/util/interpolHigh.h new file mode 100644 index 00000000000..6778e5f7d27 --- /dev/null +++ b/source/blender/python/manta_pp/util/interpolHigh.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Helper functions for higher order interpolation + * + ******************************************************************************/ + +#ifndef _INTERPOLHIGH_H +#define _INTERPOLHIGH_H + +#include "vectorbase.h" + +namespace Manta { + +// copied from interpol.h +#define BUILD_INDEX \ + Real px=pos.x-0.5f, py=pos.y-0.5f, pz=pos.z-0.5f; \ + int xi = (int)px; \ + int yi = (int)py; \ + int zi = (int)pz; \ + Real s1 = px-(Real)xi, s0 = 1.-s1; \ + Real t1 = py-(Real)yi, t0 = 1.-t1; \ + Real f1 = pz-(Real)zi, f0 = 1.-f1; \ + /* clamp to border */ \ + if (px < 0.) { xi = 0; s0 = 1.0; s1 = 0.0; } \ + if (py < 0.) { yi = 0; t0 = 1.0; t1 = 0.0; } \ + if (pz < 0.) { zi = 0; f0 = 1.0; f1 = 0.0; } \ + if (xi >= size.x-1) { xi = size.x-2; s0 = 0.0; s1 = 1.0; } \ + if (yi >= size.y-1) { yi = size.y-2; t0 = 0.0; t1 = 1.0; } \ + if (size.z>1) { if (zi >= size.z-1) { zi = size.z-2; f0 = 0.0; f1 = 1.0; } } \ + const int X = 1; \ + const int Y = size.x; + +template <class T> +inline T interpolCubic(const T* data, const Vec3i& size, const int Z, const Vec3& pos) { + BUILD_INDEX + int idx = xi + Y * yi + Z * zi; + DEBUG_ONLY(checkIndexInterpol(size,idx)); DEBUG_ONLY(checkIndexInterpol(size,idx+X+Y+Z)); + + return ((data[idx] *t0 + data[idx+Y] *t1) * s0 + + (data[idx+X]*t0 + data[idx+X+Y]*t1) * s1) * f0 + +((data[idx+Z]*t0 + data[idx+Y+Z]*t1) * s0 + + (data[idx+X+Z]*t0 + data[idx+X+Y+Z]*t1) * s1) * f1; +} + +#undef BUILD_INDEX + +} //namespace + +#endif + + diff --git a/source/blender/python/manta_pp/util/mcubes.h b/source/blender/python/manta_pp/util/mcubes.h new file mode 100644 index 00000000000..39bd5970791 --- /dev/null +++ b/source/blender/python/manta_pp/util/mcubes.h @@ -0,0 +1,323 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Marching cubes lookup indices + * + ******************************************************************************/ + +#ifndef _MCUBES_H_ +#define _MCUBES_H_ + +static const int mcEdges[24] = { + 0,1, 1,2, 2,3, 3,0, + 4,5, 5,6, 6,7, 7,4, + 0,4, 1,5, 2,6, 3,7 }; + +static const int cubieOffsetX[8] = { 0,1,1,0, 0,1,1,0 }; +static const int cubieOffsetY[8] = { 0,0,1,1, 0,0,1,1 }; +static const int cubieOffsetZ[8] = { 0,0,0,0, 1,1,1,1 }; + +/* which edges are needed ? */ +/* cf. http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ */ +static const short mcEdgeTable[256]={ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; + +/* triangles for the 256 intersection possibilities */ +/* cf. http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ */ +static const short mcTriTable[256][16] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/util/quaternion.h b/source/blender/python/manta_pp/util/quaternion.h new file mode 100644 index 00000000000..1808bb582a6 --- /dev/null +++ b/source/blender/python/manta_pp/util/quaternion.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic quaternion class + * + ******************************************************************************/ + +#ifndef _QUATERNION_H +#define _QUATERNION_H + +#include "vectorbase.h" + +namespace Manta { + +//! Very basic quaternion class +class Quaternion { +public: + + //! default constructor + Quaternion() : x(0), y(0), z(0), w(0) {} + + //! copy constructor + Quaternion(const Quaternion& q) : x(q.x), y(q.y), z(q.z), w(q.w) {} + + //! construct a quaternion from members + Quaternion(Real _x, Real _y, Real _z, Real _w) : x(_x), y(_y), z(_z), w(_w) {} + + //! construct a quaternion from imag/real parts + Quaternion(Vec3 i, Real r) : x(i.x), y(i.y), z(i.z), w(r) {} + + //! Assign operator + inline Quaternion& operator= (const Quaternion& q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + return *this; + } + + //! Assign multiplication operator + inline Quaternion& operator*= ( const Real a ) { + x *= a; + y *= a; + z *= a; + w *= a; + return *this; + } + + //! return inverse quaternion + inline Quaternion inverse() const { + Real mag = 1.0/(x*x+y*y+z*z+w*w); + return Quaternion(-x*mag,-y*mag,-z*mag,w*mag); + } + + //! imaginary part accessor + inline Vec3 imag() { return Vec3(x,y,z); } + + // imaginary part + Real x; + Real y; + Real z; + + // real part + Real w; +}; + + +//! Multiplication operator +inline Quaternion operator* ( const Quaternion &q1, const Quaternion &q2 ) { + return Quaternion ( q2.w * q1.x + q2.x * q1.w + q2.y * q1.z - q2.z * q1.y, + q2.w * q1.y + q2.y * q1.w + q2.z * q1.x - q2.x * q1.z, + q2.w * q1.z + q2.z * q1.w + q2.x * q1.y - q2.y * q1.x, + q2.w * q1.w - q2.x * q1.x - q2.y * q1.y - q2.z * q1.z ); +} + +//! Multiplication operator +inline Quaternion operator* ( const Quaternion &q, const Real a ) { + return Quaternion ( q.x*a, q.y*a, q.z*a, q.w*a); +} + +} // namespace + +#endif diff --git a/source/blender/python/manta_pp/util/randomstream.h b/source/blender/python/manta_pp/util/randomstream.h new file mode 100644 index 00000000000..23380a4c10f --- /dev/null +++ b/source/blender/python/manta_pp/util/randomstream.h @@ -0,0 +1,377 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Random numbers + * + * Based on GPL code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus + * Richard J. Wagner v0.5 7 November 2000 rjwagner@writeme.com + * + ******************************************************************************/ + +#ifndef _RANDOMSTREAM_H +#define _RANDOMSTREAM_H + +namespace Manta { + +#include <iostream> +#include <limits.h> +#include <stdio.h> +#include <time.h> +#include <math.h> +#include "vectorbase.h" + +class MTRand { + // Data + public: + typedef unsigned long uint32; // unsigned integer type, at least 32 bits + + enum { N = 624 }; // length of state vector + enum { SAVE = N + 1 }; // length of array for save() + + protected: + enum { M = 397 }; // period parameter + + uint32 state[N]; // internal state + uint32 *pNext; // next value to get from state + int left; // number of values left before reload needed + + + //Methods + public: + MTRand( const uint32& oneSeed ); // initialize with a simple uint32 + MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array + MTRand(); // auto-initialize with /dev/urandom or time() and clock() + + // Do NOT use for CRYPTOGRAPHY without securely hashing several returned + // values together, otherwise the generator state can be learned after + // reading 624 consecutive values. + + // Access to 32-bit random numbers + double rand(); // real number in [0,1] + double rand( const double& n ); // real number in [0,n] + double randExc(); // real number in [0,1) + double randExc( const double& n ); // real number in [0,n) + double randDblExc(); // real number in (0,1) + double randDblExc( const double& n ); // real number in (0,n) + uint32 randInt(); // integer in [0,2^32-1] + uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 + double operator()() { return rand(); } // same as rand() + + // Access to 53-bit random numbers (capacity of IEEE double precision) + double rand53(); // real number in [0,1) + + // Access to nonuniform random number distributions + double randNorm( const double& mean = 0.0, const double& variance = 1.0 ); + + // Re-seeding functions with same behavior as initializers + void seed( const uint32 oneSeed ); + void seed( uint32 *const bigSeed, const uint32 seedLength = N ); + void seed(); + + // Saving and loading generator state + void save( uint32* saveArray ) const; // to array of size SAVE + void load( uint32 *const loadArray ); // from such array + friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); + friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); + + protected: + void initialize( const uint32 oneSeed ); + void reload(); + uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } + uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } + uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } + uint32 mixBits( const uint32& u, const uint32& v ) const { + return hiBit(u) | loBits(v); + } + uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const { + return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); + } + static uint32 hash( time_t t, clock_t c ); +}; + + +inline MTRand::MTRand( const uint32& oneSeed ) + { seed(oneSeed); } + +inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) + { seed(bigSeed,seedLength); } + +inline MTRand::MTRand() + { seed(); } + +inline double MTRand::rand() + { return double(randInt()) * (1.0/4294967295.0); } + +inline double MTRand::rand( const double& n ) + { return rand() * n; } + +inline double MTRand::randExc() + { return double(randInt()) * (1.0/4294967296.0); } + +inline double MTRand::randExc( const double& n ) + { return randExc() * n; } + +inline double MTRand::randDblExc() + { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } + +inline double MTRand::randDblExc( const double& n ) + { return randDblExc() * n; } + +inline double MTRand::rand53() +{ + uint32 a = randInt() >> 5, b = randInt() >> 6; + return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada +} + +inline double MTRand::randNorm( const double& mean, const double& variance ) +{ + // Return a real number from a normal (Gaussian) distribution with given + // mean and variance by Box-Muller method + double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; + double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); + return mean + r * cos(phi); +} + +inline MTRand::uint32 MTRand::randInt() +{ + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + + if( left == 0 ) reload(); + --left; + + register uint32 s1; + s1 = *pNext++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680UL; + s1 ^= (s1 << 15) & 0xefc60000UL; + return ( s1 ^ (s1 >> 18) ); +} + +inline MTRand::uint32 MTRand::randInt( const uint32& n ) +{ + // Find which bits are used in n + // Optimized by Magnus Jonsson (magnus@smartelectronix.com) + uint32 used = n; + used |= used >> 1; + used |= used >> 2; + used |= used >> 4; + used |= used >> 8; + used |= used >> 16; + + // Draw numbers until one is found in [0,n] + uint32 i; + do + i = randInt() & used; // toss unused bits to shorten search + while( i > n ); + return i; +} + + +inline void MTRand::seed( const uint32 oneSeed ) +{ + // Seed the generator with a simple uint32 + initialize(oneSeed); + reload(); +} + + +inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) +{ + // Seed the generator with an array of uint32's + // There are 2^19937-1 possible initial states. This function allows + // all of those to be accessed by providing at least 19937 bits (with a + // default seed length of N = 624 uint32's). Any bits above the lower 32 + // in each element are discarded. + // Just call seed() if you want to get array from /dev/urandom + initialize(19650218UL); + const unsigned int Nenum = N; + register int i = 1; + register uint32 j = 0; + register int k = ( Nenum > seedLength ? Nenum : seedLength ); + for( ; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); + state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; + state[i] &= 0xffffffffUL; + ++i; ++j; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + if( j >= seedLength ) j = 0; + } + for( k = N - 1; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); + state[i] -= i; + state[i] &= 0xffffffffUL; + ++i; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + } + state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array + reload(); +} + + +inline void MTRand::seed() +{ + // Seed the generator with an array from /dev/urandom if available + // Otherwise use a hash of time() and clock() values + + // First try getting an array from /dev/urandom + FILE* urandom = fopen( "/dev/urandom", "rb" ); + if( urandom ) + { + uint32 bigSeed[N]; + register uint32 *s = bigSeed; + register int i = N; + register bool success = true; + while( success && i-- ) + success = fread( s++, sizeof(uint32), 1, urandom ); + fclose(urandom); + if( success ) { seed( bigSeed, N ); return; } + } + + // Was not successful, so use time() and clock() instead + seed( hash( time(NULL), clock() ) ); +} + + +inline void MTRand::initialize( const uint32 intseed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register uint32 *s = state; + register uint32 *r = state; + register int i = 1; + *s++ = intseed & 0xffffffffUL; + for( ; i < N; ++i ) + { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; + r++; + } +} + + +inline void MTRand::reload() +{ + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + register uint32 *p = state; + register int i; + for( i = N - M; i--; ++p ) + *p = twist( p[M], p[0], p[1] ); + for( i = M; --i; ++p ) + *p = twist( p[M-N], p[0], p[1] ); + *p = twist( p[M-N], p[0], state[0] ); + + left = N, pNext = state; +} + + +inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= UCHAR_MAX + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= UCHAR_MAX + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + + +inline void MTRand::save( uint32* saveArray ) const +{ + register uint32 *sa = saveArray; + register const uint32 *s = state; + register int i = N; + for( ; i--; *sa++ = *s++ ) {} + *sa = left; +} + + +inline void MTRand::load( uint32 *const loadArray ) +{ + register uint32 *s = state; + register uint32 *la = loadArray; + register int i = N; + for( ; i--; *s++ = *la++ ) {} + left = *la; + pNext = &state[N-left]; +} + + +inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) +{ + register const MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; os << *s++ << "\t" ) {} + return os << mtrand.left; +} + + +inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) +{ + register MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; is >> *s++ ) {} + is >> mtrand.left; + mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; + return is; +} + +// simple interface to mersenne twister +class RandomStream +{ +public: + inline RandomStream(long seed) : mtr(seed) {} ; + ~RandomStream() {} + + /*! get a random number from the stream */ + inline double getDouble( void ) { return mtr.rand(); }; + inline float getFloat ( void ) { return (float)mtr.rand(); }; + + inline float getFloat( float min, float max ) { return mtr.rand(max-min) + min; }; + inline float getRandNorm( float mean, float var) { return mtr.randNorm(mean, var); }; + + #if FLOATINGPOINT_PRECISION==1 + inline Real getReal() { return getFloat(); } + + #else + inline Real getReal() { return getDouble(); } + #endif + + inline Vec3 getVec3 () { Real a=getReal(), b=getReal(), c=getReal(); return Vec3(a,b,c); } + inline Vec3 getVec3Norm () { Vec3 a=getVec3(); normalize(a); return a; } + +private: + MTRand mtr; +}; + + +} // namespace + +#endif diff --git a/source/blender/python/manta_pp/util/solvana.h b/source/blender/python/manta_pp/util/solvana.h new file mode 100644 index 00000000000..c9c8167bceb --- /dev/null +++ b/source/blender/python/manta_pp/util/solvana.h @@ -0,0 +1,176 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Analytical solutions to some problems + * generated using MATLAB symbolic math ccode + * + ******************************************************************************/ + +#ifndef _SOLVANA_H +#define _SOLVANA_H + +//! solves the equation [e1 e2 e3; 1 1 1]*x = g using least squares +inline void SolveOverconstraint34(float e1x, float e1y, float e1z, + float e2x, float e2y, float e2z, + float e3x, float e3y, float e3z, + float g1, float g2, float g3, + float& x1, float& x2, float& x3) +{ + float e1x2 = e1x*e1x, e1y2 = e1y*e1y, e1z2 = e1z*e1z; + float e2x2 = e2x*e2x, e2y2 = e2y*e2y, e2z2 = e2z*e2z; + float e3x2 = e3x*e3x, e3y2 = e3y*e3y, e3z2 = e3z*e3z; + float e1xy = e1x*e1y, e1xz = e1x*e1z, e1yz = e1y*e1z; + float e2xy = e2x*e2y, e2xz = e2x*e2z, e2yz = e2y*e2z; + float e3xy = e3x*e3y, e3xz = e3x*e3z, e3yz = e3y*e3z; + float e12x = e1x*e2x, e12y = e1y*e2y, e12z = e1z*e2z; + float e13x = e1x*e3x, e13y = e1y*e3y, e13z = e1z*e3z; + float e23x = e2x*e3x, e23y = e2y*e3y, e23z = e2z*e3z; + float t1543 = e3y2*e2x2; + float t1544 = e3x2*e2y2; + float t1545 = e3z2*e2x2; + float t1546 = e3x2*e2z2; + float t1547 = e3z2*e2y2; + float t1548 = e3y2*e2z2; + float t1549 = e2y2*e1x2; + float t1550 = e2x2*e1y2; + float t1551 = e2z2*e1x2; + float t1552 = e2x2*e1z2; + float t1553 = e2z2*e1y2; + float t1554 = e2y2*e1z2; + float t1555 = e3y2*e1x2; + float t1556 = e3x2*e1y2; + float t1557 = e3z2*e1x2; + float t1558 = e3x2*e1z2; + float t1559 = e3z2*e1y2; + float t1560 = e3y2*e1z2; + float t1561 = e3z2*e2y2*e1x2; + float t1562 = e3y2*e2z2*e1x2; + float t1563 = e3z2*e2x2*e1y2; + float t1564 = e3x2*e2z2*e1y2; + float t1565 = e3y2*e2x2*e1z2; + float t1566 = e3x2*e2y2*e1z2; + float t1567 = e1xy*e2x*e3y*2.0; + float t1568 = e1xy*e2y*e3x*2.0; + float t1569 = e1xz*e2x*e3z*2.0; + float t1570 = e1xz*e2z*e3x*2.0; + float t1571 = e1yz*e2y*e3z*2.0; + float t1572 = e1yz*e2z*e3y*2.0; + float t1573 = e1x*e2xy*e3y*2.0; + float t1574 = e1y*e2xy*e3x*2.0; + float t1575 = e1x*e2xz*e3z*2.0; + float t1576 = e1z*e2xz*e3x*2.0; + float t1577 = e1y*e2yz*e3z*2.0; + float t1578 = e1z*e2yz*e3y*2.0; + float t1579 = e1x*e2y*e3xy*2.0; + float t1580 = e1y*e2x*e3xy*2.0; + float t1581 = e1x*e2z*e3xz*2.0; + float t1582 = e1z*e2x*e3xz*2.0; + float t1583 = e1y*e2z*e3yz*2.0; + float t1584 = e1z*e2y*e3yz*2.0; + float t1585 = e1xy*e2xz*e3yz*2.0; + float t1586 = e1xy*e2yz*e3xz*2.0; + float t1587 = e1xz*e2xy*e3yz*2.0; + float t1588 = e1xz*e2yz*e3xy*2.0; + float t1589 = e1yz*e2xy*e3xz*2.0; + float t1590 = e1yz*e2xz*e3xy*2.0; + float t1596 = e12x*e3y2*2.0; + float t1597 = e13x*e2y2*2.0; + float t1598 = e23x*e1y2*2.0; + float t1599 = e12x*e3z2*2.0; + float t1600 = e13x*e2z2*2.0; + float t1601 = e12y*e3x2*2.0; + float t1602 = e13y*e2x2*2.0; + float t1603 = e23y*e1x2*2.0; + float t1604 = e23x*e1z2*2.0; + float t1605 = e12y*e3z2*2.0; + float t1606 = e13y*e2z2*2.0; + float t1607 = e12z*e3x2*2.0; + float t1608 = e13z*e2x2*2.0; + float t1609 = e23z*e1x2*2.0; + float t1610 = e23y*e1z2*2.0; + float t1611 = e12z*e3y2*2.0; + float t1612 = e13z*e2y2*2.0; + float t1613 = e23z*e1y2*2.0; + float t1614 = e1xy*e2xy*2.0; + float t1615 = e1xz*e2xz*2.0; + float t1616 = e1yz*e2yz*2.0; + float t1617 = e1xy*e3xy*2.0; + float t1618 = e1xz*e3xz*2.0; + float t1619 = e1yz*e3yz*2.0; + float t1620 = e2xy*e3xy*2.0; + float t1621 = e2xz*e3xz*2.0; + float t1622 = e2yz*e3yz*2.0; + float t1623 = e1xy*e2xy*e3z2*2.0; + float t1624 = e1xz*e2xz*e3y2*2.0; + float t1625 = e1yz*e2yz*e3x2*2.0; + float t1626 = e1xy*e3xy*e2z2*2.0; + float t1627 = e1xz*e3xz*e2y2*2.0; + float t1628 = e1yz*e3yz*e2x2*2.0; + float t1629 = e2xy*e3xy*e1z2*2.0; + float t1630 = e2xz*e3xz*e1y2*2.0; + float t1631 = e2yz*e3yz*e1x2*2.0; + float t1591 = t1550+t1551+t1560+t1543+t1552+t1561+t1570+t1544+t1553+t1562+t1571+t1580+t1545+t1554+t1563+t1572+t1581+t1590+t1546+t1555+t1564+t1573+t1582+t1547+t1556+t1565+t1574+t1583+t1548+t1557+t1566+t1575+t1584+t1549+t1558+t1567+t1576+t1585+t1559+t1568+t1577+t1586+t1569+t1578+t1587-t1596+t1579+t1588-t1597+t1589-t1598-t1599-t1600-t1601-t1610-t1602-t1611-t1620-t1603-t1612-t1621-t1630-t1604-t1613-t1622-t1631-t1605-t1614-t1623-t1606-t1615-t1624-t1607-t1616-t1625-t1608-t1617-t1626-t1609-t1618-t1627-t1619-t1628-t1629; + float t1592 = 1.0/t1591; + float t1635 = e13x*e2y2; + float t1636 = e13x*e2z2; + float t1637 = e13y*e2x2; + float t1638 = e13y*e2z2; + float t1639 = e13z*e2x2; + float t1640 = e13z*e2y2; + float t1653 = e23x*2.0; + float t1654 = e23y*2.0; + float t1655 = e23z*2.0; + float t1641 = e3x2+e3z2+e3y2+e2y2+t1543+e2z2+t1544+e2x2+t1545+t1546+t1547+t1548-t1620-t1621-t1622-t1653-t1654-t1655; + float t1642 = e12x*e3y2; + float t1643 = e12x*e3z2; + float t1644 = e12y*e3x2; + float t1645 = e12y*e3z2; + float t1646 = e12z*e3x2; + float t1647 = e12z*e3y2; + float t1656 = e1x*e2y*e3xy; + float t1657 = e1y*e2x*e3xy; + float t1658 = e1x*e2z*e3xz; + float t1659 = e1z*e2x*e3xz; + float t1660 = e1y*e2z*e3yz; + float t1661 = e1z*e2y*e3yz; + float t1648 = e3x2+e3z2+e3y2-e13x-e13y-e13z+e12x-e23y+e12y+t1642-e23z-t1660+e12z+t1643-t1661+t1644+t1645+t1646+t1647-t1656-t1657-e23x-t1658-t1659; + float t1679 = e1x*e2xy*e3y; + float t1680 = e1y*e2xy*e3x; + float t1681 = e1x*e2xz*e3z; + float t1682 = e1z*e2xz*e3x; + float t1683 = e1y*e2yz*e3z; + float t1684 = e1z*e2yz*e3y; + float t1652 = e2y2+e2z2+e2x2+e13x+e13y+e13z+t1640-e12x-e23y-e12y-e23z-e12z+t1635-t1680+t1636-t1681+t1637-t1682+t1638-t1683+t1639-t1684-e23x-t1679; + float t1662 = e23x*e1y2; + float t1663 = e23y*e1x2; + float t1664 = e23x*e1z2; + float t1665 = e23z*e1x2; + float t1666 = e23y*e1z2; + float t1667 = e23z*e1y2; + float t1670 = e1xy*e2x*e3y; + float t1671 = e1xy*e2y*e3x; + float t1672 = e1xz*e2x*e3z; + float t1673 = e1xz*e2z*e3x; + float t1674 = e1yz*e2y*e3z; + float t1675 = e1yz*e2z*e3y; + float t1668 = e1x2+e1y2+e1z2-e13x-e13y-e13z-e12x+e23y-e12y+e23z-e12z-t1670+t1662-t1671+t1663-t1672+t1664-t1673+t1665-t1674+t1666-t1675+e23x+t1667; + float t1676 = e13x*2.0; + float t1677 = e13y*2.0; + float t1678 = e13z*2.0; + float t1669 = e3x2+e3z2+e3y2+t1560+e1x2+t1555+e1y2+t1556+e1z2+t1557+t1558+t1559-t1617-t1618-t1619-t1676-t1677-t1678; + float t1686 = e12x*2.0; + float t1687 = e12y*2.0; + float t1688 = e12z*2.0; + float t1685 = t1550+t1551+e2y2+t1552+e2z2+t1553+e2x2+t1554+e1x2+e1y2+e1z2+t1549-t1614-t1615-t1616-t1686-t1687-t1688; + x1 =-g2*(-e1y*t1592*t1641+e2y*t1592*t1648+e3y*t1592*t1652)-g3*(-e1z*t1592*t1641+e2z*t1592*t1648+e3z*t1592*t1652)-g1*(-e1x*t1592*t1641+e2x*t1592*t1648+e3x*t1592*(e2y2+e2z2+e2x2+e13x+e13y+e13z+t1640+t1635+t1636+t1637+t1638+t1639-e12x-e12y-e12z-e23x-e23y-e23z-e1x*e2xy*e3y-e1y*e2xy*e3x-e1x*e2xz*e3z-e1z*e2xz*e3x-e1y*e2yz*e3z-e1z*e2yz*e3y)); + x2 =-g1*(e1x*t1592*t1648-e2x*t1592*t1669+e3x*t1592*t1668)-g2*(e1y*t1592*t1648-e2y*t1592*t1669+e3y*t1592*t1668)-g3*(e1z*t1592*t1648-e2z*t1592*t1669+e3z*t1592*t1668); + x3 =-g1*(e1x*t1592*t1652+e2x*t1592*t1668-e3x*t1592*t1685)-g2*(e1y*t1592*t1652+e2y*t1592*t1668-e3y*t1592*t1685)-g3*(e1z*t1592*t1652+e2z*t1592*t1668-e3z*t1592*t1685); +} + +#endif
\ No newline at end of file diff --git a/source/blender/python/manta_pp/util/vectorbase.cpp b/source/blender/python/manta_pp/util/vectorbase.cpp new file mode 100644 index 00000000000..5f7dee46f59 --- /dev/null +++ b/source/blender/python/manta_pp/util/vectorbase.cpp @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic vector class + * + ******************************************************************************/ + +#include "vectorbase.h" +#include <limits> +#include <math.h> + +using namespace std; + +namespace Manta { + +template<> const Vector3D<int> Vector3D<int>::Zero( 0, 0, 0 ); +template<> const Vector3D<float> Vector3D<float>::Zero( 0.f, 0.f, 0.f ); +template<> const Vector3D<double> Vector3D<double>::Zero( 0., 0., 0. ); +template<> const Vector3D<float> Vector3D<float>::Invalid( numeric_limits<float>::quiet_NaN(), numeric_limits<float>::quiet_NaN(), numeric_limits<float>::quiet_NaN() ); +template<> const Vector3D<double> Vector3D<double>::Invalid( numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN() ); +//template<> const Vector3D<int> Vector3D<int>::Invalid( -1, -1, -1 ); +template<> bool Vector3D<float>::isValid() const { return !c_isnan(x) && !c_isnan(y) && !c_isnan(z); } +template<> bool Vector3D<double>::isValid() const { return !c_isnan(x) && !c_isnan(y) && !c_isnan(z); } +//template<> bool Vector3D<int>::isValid() const { return x!=-1 || y!=-1 || z!=-1; } + + + + +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/util/vectorbase.h b/source/blender/python/manta_pp/util/vectorbase.h new file mode 100644 index 00000000000..6f24a299eb9 --- /dev/null +++ b/source/blender/python/manta_pp/util/vectorbase.h @@ -0,0 +1,585 @@ +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Basic vector class + * + ******************************************************************************/ + +#ifndef _VECTORBASE_H +#define _VECTORBASE_H + +// get rid of windos min/max defines +#if defined(WIN32) || defined(_WIN32) +# define NOMINMAX +#endif + +#include <stdio.h> +//#include <stdlib.h> +#include <string> +#include <cmath> +#include <iostream> +#include "../general.h" + +// if min/max are still around... +#if defined(WIN32) || defined(_WIN32) +# undef min +# undef max +#endif + +// redefine usage of some windows functions +#if defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +// use which fp-precision? 1=float, 2=double +#ifndef FLOATINGPOINT_PRECISION +# define FLOATINGPOINT_PRECISION 1 +#endif + +// VECTOR_EPSILON is the minimal vector length +// In order to be able to discriminate floating point values near zero, and +// to be sure not to fail a comparison because of roundoff errors, use this +// value as a threshold. +#if FLOATINGPOINT_PRECISION==1 + typedef float Real; +# define FP_REAL_MAX __FLT_MAX__ +# define VECTOR_EPSILON (1e-6f) +# define _0 0.0f +# define _1 1.0f +#else + typedef double Real; +# define FP_REAL_MAX __DBL_MAX__ +# define VECTOR_EPSILON (1e-10) +# define _0 0.0 +# define _1 1.0 +#endif + +// windos, hardcoded limits for now... +// for e.g. MSVC compiler... +// some of these defines can be needed +// for linux systems as well (e.g. FLT_MAX) +#ifndef __FLT_MAX__ +# ifdef FLT_MAX // try to use it instead +# define __FLT_MAX__ FLT_MAX +# else // FLT_MAX +# define __FLT_MAX__ 3.402823466e+38f +# endif // FLT_MAX +#endif // __FLT_MAX__ +#ifndef __DBL_MAX__ +# ifdef DBL_MAX // try to use it instead +# define __DBL_MAX__ DBL_MAX +# else // DBL_MAX +# define __DBL_MAX__ 1.7976931348623158e+308 +# endif // DBL_MAX +#endif // __DBL_MAX__ + +#ifndef M_PI +# define M_PI 3.1415926536 +# define M_E 2.7182818284 +#endif + +namespace Manta +{ + +//! Basic inlined vector class +template<class S> +class Vector3D +{ +public: + //! Constructor + inline Vector3D() : x(0),y(0),z(0) {} + + //! Copy-Constructor + inline Vector3D ( const Vector3D<S> &v ) : x(v.x), y(v.y), z(v.z) {} + + //! Copy-Constructor + inline Vector3D ( const float * v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) {} + + //! Copy-Constructor + inline Vector3D ( const double * v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) {} + + //! Construct a vector from one S + inline Vector3D ( S v) : x(v), y(v), z(v) {} + + //! Construct a vector from three Ss + inline Vector3D ( S vx, S vy, S vz) : x(vx), y(vy), z(vz) {} + + // Operators + + //! Assignment operator + inline const Vector3D<S>& operator= ( const Vector3D<S>& v ) { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + //! Assignment operator + inline const Vector3D<S>& operator= ( S s ) { + x = y = z = s; + return *this; + } + //! Assign and add operator + inline const Vector3D<S>& operator+= ( const Vector3D<S>& v ) { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + //! Assign and add operator + inline const Vector3D<S>& operator+= ( S s ) { + x += s; + y += s; + z += s; + return *this; + } + //! Assign and sub operator + inline const Vector3D<S>& operator-= ( const Vector3D<S>& v ) { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + //! Assign and sub operator + inline const Vector3D<S>& operator-= ( S s ) { + x -= s; + y -= s; + z -= s; + return *this; + } + //! Assign and mult operator + inline const Vector3D<S>& operator*= ( const Vector3D<S>& v ) { + x *= v.x; + y *= v.y; + z *= v.z; + return *this; + } + //! Assign and mult operator + inline const Vector3D<S>& operator*= ( S s ) { + x *= s; + y *= s; + z *= s; + return *this; + } + //! Assign and div operator + inline const Vector3D<S>& operator/= ( const Vector3D<S>& v ) { + x /= v.x; + y /= v.y; + z /= v.z; + return *this; + } + //! Assign and div operator + inline const Vector3D<S>& operator/= ( S s ) { + x /= s; + y /= s; + z /= s; + return *this; + } + //! Negation operator + inline Vector3D<S> operator- () const { + return Vector3D<S> (-x, -y, -z); + } + + //! Get smallest component + inline S min() const { + return ( x<y ) ? ( ( x<z ) ? x:z ) : ( ( y<z ) ? y:z ); + } + //! Get biggest component + inline S max() const { + return ( x>y ) ? ( ( x>z ) ? x:z ) : ( ( y>z ) ? y:z ); + } + + //! Test if all components are zero + inline bool empty() { + return x==0 && y==0 && z==0; + } + + //! access operator + inline S& operator[] ( unsigned int i ) { + return value[i]; + } + //! constant access operator + inline const S& operator[] ( unsigned int i ) const { + return value[i]; + } + + //! debug output vector to a string + std::string toString() const; + + //! test if nans are present + bool isValid() const; + + //! actual values + union { + S value[3]; + struct { + S x; + S y; + S z; + }; + struct { + S X; + S Y; + S Z; + }; + }; + + // zero element + static const Vector3D<S> Zero, Invalid; + +protected: + +}; + +//************************************************************************ +// Additional operators +//************************************************************************ + +//! Addition operator +template<class S> +inline Vector3D<S> operator+ ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x+v2.x, v1.y+v2.y, v1.z+v2.z ); +} +//! Addition operator +template<class S, class S2> +inline Vector3D<S> operator+ ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x+s, v.y+s, v.z+s ); +} +//! Addition operator +template<class S, class S2> +inline Vector3D<S> operator+ ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( v.x+s, v.y+s, v.z+s ); +} + +//! Subtraction operator +template<class S> +inline Vector3D<S> operator- ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x-v2.x, v1.y-v2.y, v1.z-v2.z ); +} +//! Subtraction operator +template<class S, class S2> +inline Vector3D<S> operator- ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x-s, v.y-s, v.z-s ); +} +//! Subtraction operator +template<class S, class S2> +inline Vector3D<S> operator- ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s-v.x, s-v.y, s-v.z ); +} + +//! Multiplication operator +template<class S> +inline Vector3D<S> operator* ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x*v2.x, v1.y*v2.y, v1.z*v2.z ); +} +//! Multiplication operator +template<class S, class S2> +inline Vector3D<S> operator* ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x*s, v.y*s, v.z*s ); +} +//! Multiplication operator +template<class S, class S2> +inline Vector3D<S> operator* ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s*v.x, s*v.y, s*v.z ); +} + +//! Division operator +template<class S> +inline Vector3D<S> operator/ ( const Vector3D<S> &v1, const Vector3D<S> &v2 ) { + return Vector3D<S> ( v1.x/v2.x, v1.y/v2.y, v1.z/v2.z ); +} +//! Division operator +template<class S, class S2> +inline Vector3D<S> operator/ ( const Vector3D<S>& v, S2 s ) { + return Vector3D<S> ( v.x/s, v.y/s, v.z/s ); +} +//! Division operator +template<class S, class S2> +inline Vector3D<S> operator/ ( S2 s, const Vector3D<S>& v ) { + return Vector3D<S> ( s/v.x, s/v.y, s/v.z ); +} + +//! Comparison operator +template<class S> +inline bool operator== (const Vector3D<S>& s1, const Vector3D<S>& s2) { + return s1.x == s2.x && s1.y == s2.y && s1.z == s2.z; +} + +//! Comparison operator +template<class S> +inline bool operator!= (const Vector3D<S>& s1, const Vector3D<S>& s2) { + return s1.x != s2.x || s1.y != s2.y || s1.z != s2.z; +} + +//************************************************************************ +// External functions +//************************************************************************ + +//! Dot product +template<class S> +inline S dot ( const Vector3D<S> &t, const Vector3D<S> &v ) { + return t.x*v.x + t.y*v.y + t.z*v.z; +} + +//! Cross product +template<class S> +inline Vector3D<S> cross ( const Vector3D<S> &t, const Vector3D<S> &v ) { + Vector3D<S> cp ( + ( ( t.y*v.z ) - ( t.z*v.y ) ), + ( ( t.z*v.x ) - ( t.x*v.z ) ), + ( ( t.x*v.y ) - ( t.y*v.x ) ) ); + return cp; +} + +//! Project a vector into a plane, defined by its normal +/*! Projects a vector into a plane normal to the given vector, which must + have unit length. Self is modified. + \param v The vector to project + \param n The plane normal + \return The projected vector */ +template<class S> +inline const Vector3D<S>& projectNormalTo ( const Vector3D<S>& v, const Vector3D<S> &n) { + S sprod = dot (v, n); + return v - n * dot(v, n); +} + +//! Compute the magnitude (length) of the vector +template<class S> +inline S norm ( const Vector3D<S>& v ) { + S l = v.x*v.x + v.y*v.y + v.z*v.z; + return ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) ? 1. : sqrt ( l ); +} + +//! Compute squared magnitude +template<class S> +inline S normSquare ( const Vector3D<S>& v ) { + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +//! Returns a normalized vector +template<class S> +inline Vector3D<S> getNormalized ( const Vector3D<S>& v ) { + S l = v.x*v.x + v.y*v.y + v.z*v.z; + if ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) + return v; /* normalized "enough"... */ + else if ( l > VECTOR_EPSILON*VECTOR_EPSILON ) + { + S fac = 1./sqrt ( l ); + return Vector3D<S> ( v.x*fac, v.y*fac, v.z*fac ); + } + else + return Vector3D<S> ( ( S ) 0 ); +} + +//! Compute the norm of the vector and normalize it. +/*! \return The value of the norm */ +template<class S> +inline S normalize ( Vector3D<S> &v ) { + S norm; + S l = v.x*v.x + v.y*v.y + v.z*v.z; + if ( fabs ( l-1. ) < VECTOR_EPSILON*VECTOR_EPSILON ) { + norm = 1.; + } else if ( l > VECTOR_EPSILON*VECTOR_EPSILON ) { + norm = sqrt ( l ); + v *= 1./norm; + } else { + v = Vector3D<S>::Zero; + norm = 0.; + } + return ( S ) norm; +} + +//! Obtain an orthogonal vector +/*! Compute a vector that is orthonormal to the given vector. + * Nothing else can be assumed for the direction of the new vector. + * \return The orthonormal vector */ +template<class S> +Vector3D<S> getOrthogonalVector(const Vector3D<S>& v) { + // Determine the component with max. absolute value + int maxIndex= ( fabs ( v.x ) > fabs ( v.y ) ) ? 0 : 1; + maxIndex= ( fabs ( v[maxIndex] ) > fabs ( v.z ) ) ? maxIndex : 2; + + // Choose another axis than the one with max. component and project + // orthogonal to self + Vector3D<S> o ( 0.0 ); + o[ ( maxIndex+1 ) %3]= 1; + + Vector3D<S> c = cross(v, o); + normalize(c); + return c; +} + +//! Convert vector to polar coordinates +/*! Stable vector to angle conversion + *\param v vector to convert + \param phi unique angle [0,2PI] + \param theta unique angle [0,PI] + */ +template<class S> +inline void vecToAngle ( const Vector3D<S>& v, S& phi, S& theta ) +{ + if ( fabs ( v.y ) < VECTOR_EPSILON ) + theta = M_PI/2; + else if ( fabs ( v.x ) < VECTOR_EPSILON && fabs ( v.z ) < VECTOR_EPSILON ) + theta = ( v.y>=0 ) ? 0:M_PI; + else + theta = atan ( sqrt ( v.x*v.x+v.z*v.z ) /v.y ); + if ( theta<0 ) theta+=M_PI; + + if ( fabs ( v.x ) < VECTOR_EPSILON ) + phi = M_PI/2; + else + phi = atan ( v.z/v.x ); + if ( phi<0 ) phi+=M_PI; + if ( fabs ( v.z ) < VECTOR_EPSILON ) + phi = ( v.x>=0 ) ? 0 : M_PI; + else if ( v.z < 0 ) + phi += M_PI; +} + +//! Compute vector reflected at a surface +/*! Compute a vector, that is self (as an incoming vector) + * reflected at a surface with a distinct normal vector. + * Note that the normal is reversed, if the scalar product with it is positive. + \param t The incoming vector + \param n The surface normal + \return The new reflected vector + */ +template<class S> +inline Vector3D<S> reflectVector ( const Vector3D<S>& t, const Vector3D<S>& n ) { + Vector3D<S> nn= ( dot ( t, n ) > 0.0 ) ? ( n*-1.0 ) : n; + return ( t - nn * ( 2.0 * dot ( nn, t ) ) ); +} + +//! Compute vector refracted at a surface +/*! \param t The incoming vector + * \param n The surface normal + * \param nt The "inside" refraction index + * \param nair The "outside" refraction index + * \param refRefl Set to 1 on total reflection + * \return The refracted vector +*/ +template<class S> +inline Vector3D<S> refractVector ( const Vector3D<S> &t, const Vector3D<S> &normal, S nt, S nair, int &refRefl ) { + // from Glassner's book, section 5.2 (Heckberts method) + S eta = nair / nt; + S n = -dot ( t, normal ); + S tt = 1.0 + eta*eta* ( n*n-1.0 ); + if ( tt<0.0 ) { + // we have total reflection! + refRefl = 1; + } else { + // normal reflection + tt = eta*n - sqrt ( tt ); + return ( t*eta + normal*tt ); + } + return t; +} + +//! Outputs the object in human readable form as string +template<class S> std::string Vector3D<S>::toString() const { + char buf[256]; + snprintf ( buf,256,"[%+4.6f,%+4.6f,%+4.6f]", ( double ) ( *this ) [0], ( double ) ( *this ) [1], ( double ) ( *this ) [2] ); + // for debugging, optionally increase precision: + //snprintf ( buf,256,"[%+4.16f,%+4.16f,%+4.16f]", ( double ) ( *this ) [0], ( double ) ( *this ) [1], ( double ) ( *this ) [2] ); + return std::string ( buf ); +} + +//! Outputs the object in human readable form to stream +/*! Output format [x,y,z] */ +template<class S> +std::ostream& operator<< ( std::ostream& os, const Vector3D<S>& i ) { + os << i.toString(); + return os; +} + +//! Reads the contents of the object from a stream +/*! Input format [x,y,z] */ +template<class S> +std::istream& operator>> ( std::istream& is, Vector3D<S>& i ) { + char c; + char dummy[3]; + is >> c >> i[0] >> dummy >> i[1] >> dummy >> i[2] >> c; + return is; +} + +/**************************************************************************/ +// Define default vector alias +/**************************************************************************/ + +//! 3D vector class of type Real (typically float) +typedef Vector3D<Real> Vec3; + +//! 3D vector class of type int +typedef Vector3D<int> Vec3i; + +//! convert to Real Vector +template<class T> inline Vec3 toVec3 ( T v ) { + return Vec3 ( v[0],v[1],v[2] ); +} + +//! convert to int Vector +template<class T> inline Vec3i toVec3i ( T v ) { + return Vec3i ( ( int ) v[0], ( int ) v[1], ( int ) v[2] ); +} + +//! convert to int Vector +template<class T> inline Vec3i toVec3i ( T v0, T v1, T v2 ) { + return Vec3i ( ( int ) v0, ( int ) v1, ( int ) v2 ); +} + +//! round, and convert to int Vector +template<class T> inline Vec3i toVec3iRound ( T v ) { + return Vec3i ( ( int ) round ( v[0] ), ( int ) round ( v[1] ), ( int ) round ( v[2] ) ); +} + +//! convert to int Vector if values are close enough to an int +template<class T> inline Vec3i toVec3iChecked ( T v ) { + Vec3i ret; + for (size_t i=0; i<3; i++) { + Real a = v[i]; + if (fabs(a-floor(a+0.5)) > 1e-5) + errMsg("argument is not an int, cannot convert"); + ret[i] = (int) (a+0.5); + } + return ret; +} + +//! convert to double Vector +template<class T> inline Vector3D<double> toVec3d ( T v ) { + return Vector3D<double> ( v[0], v[1], v[2] ); +} + +//! convert to float Vector +template<class T> inline Vector3D<float> toVec3f ( T v ) { + return Vector3D<float> ( v[0], v[1], v[2] ); +} + + +/**************************************************************************/ +// Specializations for common math functions +/**************************************************************************/ + +template<> inline Vec3 clamp<Vec3>(const Vec3& a, const Vec3& b, const Vec3& c) { + return Vec3 ( clamp(a.x, b.x, c.x), + clamp(a.y, b.y, c.y), + clamp(a.z, b.z, c.z) ); +} +template<> inline Vec3 safeDivide<Vec3>(const Vec3 &a, const Vec3& b) { + return Vec3(safeDivide(a.x,b.x), safeDivide(a.y,b.y), safeDivide(a.z,b.z)); +} +template<> inline Vec3 nmod<Vec3>(const Vec3& a, const Vec3& b) { + return Vec3(nmod(a.x,b.x),nmod(a.y,b.y),nmod(a.z,b.z)); +} + +}; // namespace + + +#endif diff --git a/source/blender/python/manta_pp/vortexpart.cpp b/source/blender/python/manta_pp/vortexpart.cpp new file mode 100644 index 00000000000..32e2fd2053e --- /dev/null +++ b/source/blender/python/manta_pp/vortexpart.cpp @@ -0,0 +1,109 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex particles + * + ******************************************************************************/ + +#include "vortexpart.h" +#include "integrator.h" +#include "mesh.h" + +using namespace std; +namespace Manta { + +// vortex particle effect: (cyl coord around wp) +// u = -|wp|*rho*exp( (-rho^2-z^2)/(2sigma^2) ) e_phi +inline Vec3 VortexKernel(const Vec3& p, const vector<VortexParticleData>& vp, Real scale) { + Vec3 u(_0); + for (size_t i=0; i<vp.size(); i++) { + if (vp[i].flag & ParticleBase::PDELETE) continue; + + // cutoff radius + const Vec3 r = p - vp[i].pos; + const Real rlen2 = normSquare(r); + const Real sigma2 = square(vp[i].sigma); + if (rlen2 > 6.0 * sigma2 || rlen2 < 1e-8) continue; + + // split vortex strength + Vec3 vortNorm = vp[i].vorticity; + Real strength = normalize(vortNorm) * scale; + + // transform in cylinder coordinate system + const Real rlen = sqrt(rlen2); + const Real z = dot(r, vortNorm); + const Vec3 ePhi = cross(r, vortNorm) / rlen; + const Real rho2 = rlen2 - z*z; + + Real vortex = 0; + if (rho2 > 1e-10) { + // evaluate Kernel + vortex = strength * sqrt(rho2) * exp (rlen2 * -0.5/sigma2); + } + u += vortex * ePhi; + } + return u; +} + + + struct KnVpAdvectMesh : public KernelBase { KnVpAdvectMesh(vector<Node>& nodes, const vector<VortexParticleData>& vp, Real scale) : KernelBase(nodes.size()) ,nodes(nodes),vp(vp),scale(scale) ,u((size)) { run(); } inline void op(int idx, vector<Node>& nodes, const vector<VortexParticleData>& vp, Real scale ,vector<Vec3> & u) { + if (nodes[idx].flags & Mesh::NfFixed) + u[idx] = _0; + else + u[idx] = VortexKernel(nodes[idx].pos, vp, scale); +} inline operator vector<Vec3> () { return u; } inline vector<Vec3> & getRet() { return u; } inline vector<Node>& getArg0() { return nodes; } typedef vector<Node> type0;inline const vector<VortexParticleData>& getArg1() { return vp; } typedef vector<VortexParticleData> type1;inline Real& getArg2() { return scale; } typedef Real type2; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, nodes,vp,scale,u); } vector<Node>& nodes; const vector<VortexParticleData>& vp; Real scale; vector<Vec3> u; }; + + + struct KnVpAdvectSelf : public KernelBase { KnVpAdvectSelf(vector<VortexParticleData>& vp, Real scale) : KernelBase(vp.size()) ,vp(vp),scale(scale) ,u((size)) { run(); } inline void op(int idx, vector<VortexParticleData>& vp, Real scale ,vector<Vec3> & u) { + if (vp[idx].flag & ParticleBase::PDELETE) + u[idx] = _0; + else + u[idx] = VortexKernel(vp[idx].pos, vp, scale); +} inline operator vector<Vec3> () { return u; } inline vector<Vec3> & getRet() { return u; } inline vector<VortexParticleData>& getArg0() { return vp; } typedef vector<VortexParticleData> type0;inline Real& getArg1() { return scale; } typedef Real type1; void run() { const int _sz = size; for (int i=0; i < _sz; i++) op(i, vp,scale,u); } vector<VortexParticleData>& vp; Real scale; vector<Vec3> u; }; + +VortexParticleSystem::VortexParticleSystem(FluidSolver* parent) : + ParticleSystem<VortexParticleData>(parent) +{ +} + +void VortexParticleSystem::advectSelf(Real scale, int integrationMode) { + KnVpAdvectSelf kernel(mData, scale* getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +void VortexParticleSystem::applyToMesh(Mesh& mesh, Real scale, int integrationMode) { + KnVpAdvectMesh kernel(mesh.getNodeData(), mData, scale* getParent()->getDt()); + integratePointSet( kernel, integrationMode); +} + +ParticleBase* VortexParticleSystem::clone() { + VortexParticleSystem* nm = new VortexParticleSystem(getParent()); + compress(); + + nm->mData = mData; + nm->setName(getName()); + return nm; +} + + + +} // namespace + + diff --git a/source/blender/python/manta_pp/vortexpart.h b/source/blender/python/manta_pp/vortexpart.h new file mode 100644 index 00000000000..47880678163 --- /dev/null +++ b/source/blender/python/manta_pp/vortexpart.h @@ -0,0 +1,56 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex particles + * + ******************************************************************************/ + +#ifndef _VORTEXPART_H +#define _VORTEXPART_H + +#include "particle.h" + +namespace Manta { +class Mesh; + +struct VortexParticleData { + VortexParticleData() : pos(_0),vorticity(_0),sigma(0),flag(0) {} + VortexParticleData(const Vec3& p, const Vec3& v, Real sig) : pos(p),vorticity(v),sigma(sig),flag(0) {} + Vec3 pos, vorticity; + Real sigma; + int flag; + static ParticleBase::SystemType getType() { return ParticleBase::VORTEX; } +}; + +//! Vortex particles +class VortexParticleSystem : public ParticleSystem<VortexParticleData> {public: + VortexParticleSystem(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "VortexParticleSystem::VortexParticleSystem" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new VortexParticleSystem(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"VortexParticleSystem::VortexParticleSystem" ); return 0; } catch(std::exception& e) { pbSetError("VortexParticleSystem::VortexParticleSystem",e.what()); return -1; } } + + void advectSelf(Real scale=1.0, int integrationMode=IntRK4); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); VortexParticleSystem* pbo = dynamic_cast<VortexParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "VortexParticleSystem::advectSelf"); PyObject *_retval = 0; { ArgLocker _lock; Real scale = _args.getOpt<Real >("scale",0,1.0,&_lock); int integrationMode = _args.getOpt<int >("integrationMode",1,IntRK4,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->advectSelf(scale,integrationMode); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"VortexParticleSystem::advectSelf"); return _retval; } catch(std::exception& e) { pbSetError("VortexParticleSystem::advectSelf",e.what()); return 0; } } + void applyToMesh(Mesh& mesh, Real scale=1.0, int integrationMode=IntRK4); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); VortexParticleSystem* pbo = dynamic_cast<VortexParticleSystem*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "VortexParticleSystem::applyToMesh"); PyObject *_retval = 0; { ArgLocker _lock; Mesh& mesh = *_args.getPtr<Mesh >("mesh",0,&_lock); Real scale = _args.getOpt<Real >("scale",1,1.0,&_lock); int integrationMode = _args.getOpt<int >("integrationMode",2,IntRK4,&_lock); pbo->_args.copy(_args); _retval = getPyNone(); pbo->applyToMesh(mesh,scale,integrationMode); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"VortexParticleSystem::applyToMesh"); return _retval; } catch(std::exception& e) { pbSetError("VortexParticleSystem::applyToMesh",e.what()); return 0; } } + virtual ParticleBase* clone(); public: PbArgs _args;} +#define _C_VortexParticleSystem +; + +} // namespace + + +#endif + diff --git a/source/blender/python/manta_pp/vortexpart.h.reg b/source/blender/python/manta_pp/vortexpart.h.reg new file mode 100644 index 00000000000..8d36d048fbd --- /dev/null +++ b/source/blender/python/manta_pp/vortexpart.h.reg @@ -0,0 +1,7 @@ +#include "vortexpart.h" ++VortexParticleSystem^ static const Pb::Register _R_$IDX$ ("VortexParticleSystem","VortexParticleSystem","ParticleSystem<$BT$>"); template<> const char* Namify<VortexParticleSystem >::S = "VortexParticleSystem"; +>VortexParticleSystem^ +@VortexParticleSystem^^ParticleSystem^VortexParticleData ++VortexParticleSystem^ static const Pb::Register _R_$IDX$ ("VortexParticleSystem","VortexParticleSystem",VortexParticleSystem::_W_0); ++VortexParticleSystem^ static const Pb::Register _R_$IDX$ ("VortexParticleSystem","advectSelf",VortexParticleSystem::_W_1); ++VortexParticleSystem^ static const Pb::Register _R_$IDX$ ("VortexParticleSystem","applyToMesh",VortexParticleSystem::_W_2); diff --git a/source/blender/python/manta_pp/vortexpart.h.reg.cpp b/source/blender/python/manta_pp/vortexpart.h.reg.cpp new file mode 100644 index 00000000000..99b1888f316 --- /dev/null +++ b/source/blender/python/manta_pp/vortexpart.h.reg.cpp @@ -0,0 +1,32 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "vortexpart.h" +namespace Manta { +#ifdef _C_ParticleSystem + static const Pb::Register _R_0 ("ParticleSystem<VortexParticleData>","ParticleSystem<VortexParticleData>","ParticleBase"); template<> const char* Namify<ParticleSystem<VortexParticleData> >::S = "ParticleSystem<VortexParticleData>"; + static const Pb::Register _R_1 ("ParticleSystem<VortexParticleData>","ParticleSystem",ParticleSystem<VortexParticleData>::_W_2); + static const Pb::Register _R_2 ("ParticleSystem<VortexParticleData>","size",ParticleSystem<VortexParticleData>::_W_3); + static const Pb::Register _R_3 ("ParticleSystem<VortexParticleData>","setPos",ParticleSystem<VortexParticleData>::_W_4); + static const Pb::Register _R_4 ("ParticleSystem<VortexParticleData>","getPos",ParticleSystem<VortexParticleData>::_W_5); + static const Pb::Register _R_5 ("ParticleSystem<VortexParticleData>","getPosPdata",ParticleSystem<VortexParticleData>::_W_6); + static const Pb::Register _R_6 ("ParticleSystem<VortexParticleData>","setPosPdata",ParticleSystem<VortexParticleData>::_W_7); + static const Pb::Register _R_7 ("ParticleSystem<VortexParticleData>","clear",ParticleSystem<VortexParticleData>::_W_8); + static const Pb::Register _R_8 ("ParticleSystem<VortexParticleData>","advectInGrid",ParticleSystem<VortexParticleData>::_W_9); + static const Pb::Register _R_9 ("ParticleSystem<VortexParticleData>","projectOutside",ParticleSystem<VortexParticleData>::_W_10); +#endif +#ifdef _C_VortexParticleSystem + static const Pb::Register _R_10 ("VortexParticleSystem","VortexParticleSystem","ParticleSystem<VortexParticleData>"); template<> const char* Namify<VortexParticleSystem >::S = "VortexParticleSystem"; + static const Pb::Register _R_11 ("VortexParticleSystem","VortexParticleSystem",VortexParticleSystem::_W_0); + static const Pb::Register _R_12 ("VortexParticleSystem","advectSelf",VortexParticleSystem::_W_1); + static const Pb::Register _R_13 ("VortexParticleSystem","applyToMesh",VortexParticleSystem::_W_2); +#endif +}
\ No newline at end of file diff --git a/source/blender/python/manta_pp/vortexsheet.cpp b/source/blender/python/manta_pp/vortexsheet.cpp new file mode 100644 index 00000000000..c58544f5f28 --- /dev/null +++ b/source/blender/python/manta_pp/vortexsheet.cpp @@ -0,0 +1,105 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex sheets + * + ******************************************************************************/ + +#include "vortexsheet.h" +#include "solvana.h" + +using namespace std; +namespace Manta { + +// ***************************************************************************** +// VorticityChannel class members + + + +// ***************************************************************************** +// VortexSheet Mesh class members + +VortexSheetMesh::VortexSheetMesh(FluidSolver* parent) : Mesh(parent), mTexOffset(0.0f) { + addTriChannel(&mVorticity); + addNodeChannel(&mTex1); + addNodeChannel(&mTex2); + addNodeChannel(&mTurb); +} + + +Mesh* VortexSheetMesh::clone() { + VortexSheetMesh* nm = new VortexSheetMesh(mParent); + *nm = *this; + nm->setName(getName()); + return nm; +} + + +void VortexSheetMesh::calcVorticity() { + for (size_t tri = 0; tri < mTris.size(); tri++) { + VortexSheetInfo& v = mVorticity.data[tri]; + Vec3 e0 = getEdge(tri,0), e1 = getEdge(tri,1), e2 = getEdge(tri,2); + Real area = getFaceArea(tri); + + if (area < 1e-10) { + v.smokeAmount = 0; + v.vorticity = 0; + } else { + v.smokeAmount = 0; + v.vorticity = (v.circulation[0]*e0 + v.circulation[1]*e1 + v.circulation[2]*e2) / area; + } + } +} + +void VortexSheetMesh::calcCirculation() { + for (size_t tri = 0; tri < mTris.size(); tri++) { + VortexSheetInfo& v = mVorticity.data[tri]; + Vec3 e0 = getEdge(tri,0), e1 = getEdge(tri,1), e2 = getEdge(tri,2); + Real area = getFaceArea(tri); + + if (area < 1e-10 || normSquare(v.vorticity) < 1e-10) { + v.circulation = 0; + continue; + } + + float cx, cy, cz; + SolveOverconstraint34(e0.x, e0.y, e0.z, e1.x, e1.y, e1.z, e2.x, e2.y, e2.z, v.vorticity.x, v.vorticity.y, v.vorticity.z, cx, cy, cz); + v.circulation = Vec3(cx, cy, cz) * area; + } +} + +void VortexSheetMesh::resetTex1() { + for (size_t i=0; i<mNodes.size(); i++) + mTex1.data[i] = mNodes[i].pos + mTexOffset; +} + +void VortexSheetMesh::resetTex2() { + for (size_t i=0; i<mNodes.size(); i++) + mTex2.data[i] = mNodes[i].pos + mTexOffset; +} + +void VortexSheetMesh::reinitTexCoords() { + resetTex1(); + resetTex2(); +} + +}; // namespace + + diff --git a/source/blender/python/manta_pp/vortexsheet.h b/source/blender/python/manta_pp/vortexsheet.h new file mode 100644 index 00000000000..b6656d56977 --- /dev/null +++ b/source/blender/python/manta_pp/vortexsheet.h @@ -0,0 +1,98 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + + + + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011 Tobias Pfaff, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Vortex sheets + * + ******************************************************************************/ + +#ifndef _VORTEXSHEET_H +#define _VORTEXSHEET_H + +#include "mesh.h" + +namespace Manta { + +//! Stores vortex sheet info +struct VortexSheetInfo { + VortexSheetInfo() : vorticity(0.0), vorticitySmoothed(0.0), circulation(0.0), smokeAmount(1.0), smokeParticles(0.0) {} + + Vec3 vorticity; + Vec3 vorticitySmoothed; + Vec3 circulation; + Real smokeAmount, smokeParticles; +}; + +//! Manages vortex sheet info +struct VorticityChannel : public SimpleTriChannel<VortexSheetInfo> { + virtual TriChannel* clone() { VorticityChannel* vc = new VorticityChannel(); *vc = *this; return vc;} +}; + +//! Manages 3D texture coordinates +struct TexCoord3Channel : public SimpleNodeChannel<Vec3> { + virtual NodeChannel* clone() { TexCoord3Channel* tc = new TexCoord3Channel(); *tc = *this; return tc; } + + void addInterpol(int a, int b, Real alpha) { data.push_back((1.0-alpha)*data[a] + alpha*data[b]);} + void mergeWith(int node, int delnode, Real alpha) { data[node] = 0.5*(data[node]+data[delnode]); } +}; + +struct TurbulenceInfo { + TurbulenceInfo() : k(0.0), epsilon(0.0) {} + TurbulenceInfo(const TurbulenceInfo& a, const TurbulenceInfo& b, Real alpha) : k((1.0-alpha)*a.k+alpha*b.k), epsilon((1.0-alpha)*a.epsilon+alpha*b.epsilon) {} + Real k, epsilon; +}; + +//! Manages k-epsilon information +struct TurbulenceChannel : public SimpleNodeChannel<TurbulenceInfo> { + virtual NodeChannel* clone() { TurbulenceChannel* tc = new TurbulenceChannel(); *tc = *this; return tc; } + + void addInterpol(int a, int b, Real alpha) { data.push_back(TurbulenceInfo(data[a], data[b], alpha)); } + void mergeWith(int node, int delnode, Real alpha) { data[node] = TurbulenceInfo(data[node], data[delnode], 0.5); } +}; + +//! Typed Mesh with a vorticity and 2 texcoord3 channels +class VortexSheetMesh : public Mesh {public: + VortexSheetMesh(FluidSolver* parent); static int _W_0 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { PbClass* obj = Pb::objFromPy(_self); if (obj) delete obj; try { PbArgs _args(_linargs, _kwds); pbPreparePlugin(0, "VortexSheetMesh::VortexSheetMesh" ); { ArgLocker _lock; FluidSolver* parent = _args.getPtr<FluidSolver >("parent",0,&_lock); obj = new VortexSheetMesh(parent); obj->registerObject(_self, &_args); _args.check(); } pbFinalizePlugin(obj->getParent(),"VortexSheetMesh::VortexSheetMesh" ); return 0; } catch(std::exception& e) { pbSetError("VortexSheetMesh::VortexSheetMesh",e.what()); return -1; } } + virtual Mesh* clone(); + + virtual MeshType getType() { return TypeVortexSheet; } + + inline VortexSheetInfo& sheet(int i) { return mVorticity.data[i]; }; + inline Vec3& tex1(int i) { return mTex1.data[i]; } + inline Vec3& tex2(int i) { return mTex2.data[i]; } + inline TurbulenceInfo& turb(int i) { return mTurb.data[i]; } + void setReferenceTexOffset(const Vec3& ref) { mTexOffset = ref; } + void resetTex1(); + void resetTex2(); + + void calcCirculation(); static PyObject* _W_1 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); VortexSheetMesh* pbo = dynamic_cast<VortexSheetMesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "VortexSheetMesh::calcCirculation"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->calcCirculation(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"VortexSheetMesh::calcCirculation"); return _retval; } catch(std::exception& e) { pbSetError("VortexSheetMesh::calcCirculation",e.what()); return 0; } } + void calcVorticity(); static PyObject* _W_2 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); VortexSheetMesh* pbo = dynamic_cast<VortexSheetMesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "VortexSheetMesh::calcVorticity"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->calcVorticity(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"VortexSheetMesh::calcVorticity"); return _retval; } catch(std::exception& e) { pbSetError("VortexSheetMesh::calcVorticity",e.what()); return 0; } } + void reinitTexCoords(); static PyObject* _W_3 (PyObject* _self, PyObject* _linargs, PyObject* _kwds) { try { PbArgs _args(_linargs, _kwds); VortexSheetMesh* pbo = dynamic_cast<VortexSheetMesh*>(Pb::objFromPy(_self)); pbPreparePlugin(pbo->getParent(), "VortexSheetMesh::reinitTexCoords"); PyObject *_retval = 0; { ArgLocker _lock; pbo->_args.copy(_args); _retval = getPyNone(); pbo->reinitTexCoords(); pbo->_args.check(); } pbFinalizePlugin(pbo->getParent(),"VortexSheetMesh::reinitTexCoords"); return _retval; } catch(std::exception& e) { pbSetError("VortexSheetMesh::reinitTexCoords",e.what()); return 0; } } + +protected: + Vec3 mTexOffset; + VorticityChannel mVorticity; + TexCoord3Channel mTex1, mTex2; TurbulenceChannel mTurb; public: PbArgs _args;} +#define _C_VortexSheetMesh +; + +}; // namespace + +#endif + diff --git a/source/blender/python/manta_pp/vortexsheet.h.reg b/source/blender/python/manta_pp/vortexsheet.h.reg new file mode 100644 index 00000000000..d5ad6b64113 --- /dev/null +++ b/source/blender/python/manta_pp/vortexsheet.h.reg @@ -0,0 +1,7 @@ +#include "vortexsheet.h" ++VortexSheetMesh^ static const Pb::Register _R_$IDX$ ("VortexSheetMesh","VortexSheetMesh","Mesh"); template<> const char* Namify<VortexSheetMesh >::S = "VortexSheetMesh"; +>VortexSheetMesh^ ++VortexSheetMesh^ static const Pb::Register _R_$IDX$ ("VortexSheetMesh","VortexSheetMesh",VortexSheetMesh::_W_0); ++VortexSheetMesh^ static const Pb::Register _R_$IDX$ ("VortexSheetMesh","calcCirculation",VortexSheetMesh::_W_1); ++VortexSheetMesh^ static const Pb::Register _R_$IDX$ ("VortexSheetMesh","calcVorticity",VortexSheetMesh::_W_2); ++VortexSheetMesh^ static const Pb::Register _R_$IDX$ ("VortexSheetMesh","reinitTexCoords",VortexSheetMesh::_W_3); diff --git a/source/blender/python/manta_pp/vortexsheet.h.reg.cpp b/source/blender/python/manta_pp/vortexsheet.h.reg.cpp new file mode 100644 index 00000000000..d8dcc775ed9 --- /dev/null +++ b/source/blender/python/manta_pp/vortexsheet.h.reg.cpp @@ -0,0 +1,21 @@ + + + + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep link). + + + + +#include "vortexsheet.h" +namespace Manta { +#ifdef _C_VortexSheetMesh + static const Pb::Register _R_0 ("VortexSheetMesh","VortexSheetMesh","Mesh"); template<> const char* Namify<VortexSheetMesh >::S = "VortexSheetMesh"; + static const Pb::Register _R_1 ("VortexSheetMesh","VortexSheetMesh",VortexSheetMesh::_W_0); + static const Pb::Register _R_2 ("VortexSheetMesh","calcCirculation",VortexSheetMesh::_W_1); + static const Pb::Register _R_3 ("VortexSheetMesh","calcVorticity",VortexSheetMesh::_W_2); + static const Pb::Register _R_4 ("VortexSheetMesh","reinitTexCoords",VortexSheetMesh::_W_3); +#endif +}
\ No newline at end of file diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 36b9f8ae362..318b376d8c1 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -138,6 +138,10 @@ if(WITH_MOD_SMOKE) add_definitions(-DWITH_SMOKE) endif() +if(WITH_MOD_MANTA) + add_definitions(-DWITH_MANTA) +endif() + if(WITH_FREESTYLE) list(APPEND INC ../freestyle diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 25ee4f2ef21..4451264c02a 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -137,6 +137,7 @@ endif() bf_intern_guardedalloc bf_intern_memutil bf_python_ext + bf_python_manta bf_python_mathutils bf_python_bmesh bf_intern_utfconv diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 57e67a971d3..9f3ee8b3935 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -994,6 +994,19 @@ add_dependencies(blender makesdna) setup_blender_sorted_libs() +# ----------------------------------------------------------------------------- +# Mantaflow Force linking +#PR removed -Wl, -force_load +#SET(MANTA_LINK_LIBRARIES -rdynamic -Wl,-force_load bf_python_manta) +if(APPLE) + SET(MANTA_LINK_LIBRARIES -force_load bf_python_manta ) +else() + SET(MANTA_LINK_LIBRARIES -Wl,--whole-archive bf_python_manta -Wl,--no-whole-archive ) +endif() +target_link_libraries(blender ${MANTA_LINK_LIBRARIES}) + +# ----------------------------------------------------------------------------- +# Setup link libs target_link_libraries(blender ${BLENDER_SORTED_LIBS}) setup_liblinks(blender) |