Files
test/intern/smoke/intern/FLUID_3D_STATIC.cpp

647 lines
21 KiB
C++
Raw Normal View History

/** \file
* \ingroup smoke
2011-02-25 11:50:46 +00:00
*/
//////////////////////////////////////////////////////////////////////
// This file is part of Wavelet Turbulence.
//
// Wavelet Turbulence 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.
//
// Wavelet Turbulence 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 Wavelet Turbulence. If not, see <http://www.gnu.org/licenses/>.
//
// Copyright 2008 Theodore Kim and Nils Thuerey
//
// FLUID_3D.cpp: implementation of the static functions of the FLUID_3D class.
//
//////////////////////////////////////////////////////////////////////
// Heavy parallel optimization done. Many of the old functions now
// take begin and end parameters and process only specified part of the data.
// Some functions were divided into multiple ones.
// - MiikaH
//////////////////////////////////////////////////////////////////////
#include <zlib.h>
#include "FLUID_3D.h"
#include "IMAGE.h"
#include "WTURBULENCE.h"
#include "INTERPOLATE.h"
//////////////////////////////////////////////////////////////////////
// add a test cube of density to the center
//////////////////////////////////////////////////////////////////////
/*
void FLUID_3D::addSmokeColumn() {
addSmokeTestCase(_density, _res, 1.0);
// addSmokeTestCase(_zVelocity, _res, 1.0);
addSmokeTestCase(_heat, _res, 1.0);
if (_wTurbulence) {
addSmokeTestCase(_wTurbulence->getDensityBig(), _wTurbulence->getResBig(), 1.0);
}
}
*/
//////////////////////////////////////////////////////////////////////
// generic static version, so that it can be applied to the
// WTURBULENCE grid as well
//////////////////////////////////////////////////////////////////////
void FLUID_3D::addSmokeTestCase(float* field, Vec3Int res)
{
const int slabSize = res[0]*res[1]; int maxRes = (int)MAX3V(res);
float dx = 1.0f / (float)maxRes;
float xTotal = dx * res[0];
float yTotal = dx * res[1];
float heighMin = 0.05;
float heighMax = 0.10;
for (int y = 0; y < res[2]; y++)
for (int z = (int)(heighMin*res[2]); z <= (int)(heighMax * res[2]); z++)
for (int x = 0; x < res[0]; x++) {
float xLength = x * dx - xTotal * 0.4f;
float yLength = y * dx - yTotal * 0.5f;
float radius = sqrtf(xLength * xLength + yLength * yLength);
if (radius < 0.075f * xTotal) {
int index = x + y * res[0] + z * slabSize;
field[index] = 1.0f;
}
}
}
//////////////////////////////////////////////////////////////////////
// set x direction to Neumann boundary conditions
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setNeumannX(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int y = 0; y < res[1]; y++)
{
// left slab
index = y * res[0] + z * slabSize;
field[index] = field[index + 2];
// right slab
index = y * res[0] + z * slabSize + res[0] - 1;
field[index] = field[index - 2];
}
}
//////////////////////////////////////////////////////////////////////
// set y direction to Neumann boundary conditions
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setNeumannY(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int x = 0; x < res[0]; x++)
{
// front slab
index = x + z * slabSize;
field[index] = field[index + 2 * res[0]];
// back slab
index = x + z * slabSize + slabSize - res[0];
field[index] = field[index - 2 * res[0]];
}
}
//////////////////////////////////////////////////////////////////////
// set z direction to Neumann boundary conditions
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setNeumannZ(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
const int totalCells = res[0] * res[1] * res[2];
const int cellsslab = totalCells - slabSize;
int index;
if (zBegin == 0) {
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++)
{
// front slab
index = x + y * res[0];
field[index] = field[index + 2 * slabSize];
}
}
if (zEnd == res[2]) {
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++)
{
// back slab
index = x + y * res[0] + cellsslab;
field[index] = field[index - 2 * slabSize];
}
}
}
//////////////////////////////////////////////////////////////////////
// set x direction to zero
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setZeroX(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int y = 0; y < res[1]; y++)
{
// left slab
index = y * res[0] + z * slabSize;
field[index] = 0.0f;
// right slab
index += res[0] - 1;
field[index] = 0.0f;
}
}
//////////////////////////////////////////////////////////////////////
// set y direction to zero
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setZeroY(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int x = 0; x < res[0]; x++)
{
// bottom slab
index = x + z * slabSize;
field[index] = 0.0f;
// top slab
index += slabSize - res[0];
field[index] = 0.0f;
}
}
//////////////////////////////////////////////////////////////////////
// set z direction to zero
//////////////////////////////////////////////////////////////////////
void FLUID_3D::setZeroZ(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
const int totalCells = res[0] * res[1] * res[2];
int index = 0;
if (zBegin == 0)
{
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++, index++)
{
// front slab
field[index] = 0.0f;
}
}
if (zEnd == res[2])
{
index=0;
int indexx=0;
const int cellsslab = totalCells - slabSize;
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++, index++)
{
// back slab
indexx = index + cellsslab;
field[indexx] = 0.0f;
}
}
}
//////////////////////////////////////////////////////////////////////
// copy grid boundary
//////////////////////////////////////////////////////////////////////
void FLUID_3D::copyBorderX(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int y = 0; y < res[1]; y++)
{
// left slab
index = y * res[0] + z * slabSize;
field[index] = field[index + 1];
// right slab
index += res[0] - 1;
field[index] = field[index - 1];
}
}
void FLUID_3D::copyBorderY(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
//const int totalCells = res[0] * res[1] * res[2];
int index;
for (int z = zBegin; z < zEnd; z++)
for (int x = 0; x < res[0]; x++)
{
// bottom slab
index = x + z * slabSize;
field[index] = field[index + res[0]];
// top slab
index += slabSize - res[0];
field[index] = field[index - res[0]];
}
}
void FLUID_3D::copyBorderZ(float* field, Vec3Int res, int zBegin, int zEnd)
{
const int slabSize = res[0] * res[1];
const int totalCells = res[0] * res[1] * res[2];
int index=0;
if (zBegin == 0)
{
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++, index++)
{
field[index] = field[index + slabSize];
}
}
if (zEnd == res[2])
{
index=0;
int indexx=0;
const int cellsslab = totalCells - slabSize;
for (int y = 0; y < res[1]; y++)
for (int x = 0; x < res[0]; x++, index++)
{
// back slab
indexx = index + cellsslab;
field[indexx] = field[indexx - slabSize];
}
}
}
/////////////////////////////////////////////////////////////////////
// advect field with the semi lagrangian method
//////////////////////////////////////////////////////////////////////
void FLUID_3D::advectFieldSemiLagrange(const float dt, const float* velx, const float* vely, const float* velz,
float* oldField, float* newField, Vec3Int res, int zBegin, int zEnd)
{
const int xres = res[0];
const int yres = res[1];
const int zres = res[2];
const int slabSize = res[0] * res[1];
for (int z = zBegin; z < zEnd; z++)
for (int y = 0; y < yres; y++)
for (int x = 0; x < xres; x++)
{
const int index = x + y * xres + z * xres*yres;
// backtrace
float xTrace = x - dt * velx[index];
float yTrace = y - dt * vely[index];
float zTrace = z - dt * velz[index];
// clamp backtrace to grid boundaries
if (xTrace < 0.5f) xTrace = 0.5f;
if (xTrace > xres - 1.5f) xTrace = xres - 1.5f;
if (yTrace < 0.5f) yTrace = 0.5f;
if (yTrace > yres - 1.5f) yTrace = yres - 1.5f;
if (zTrace < 0.5f) zTrace = 0.5f;
if (zTrace > zres - 1.5f) zTrace = zres - 1.5f;
// locate neighbors to interpolate
const int x0 = (int)xTrace;
const int x1 = x0 + 1;
const int y0 = (int)yTrace;
const int y1 = y0 + 1;
const int z0 = (int)zTrace;
const int z1 = z0 + 1;
// get interpolation weights
const float s1 = xTrace - x0;
const float s0 = 1.0f - s1;
const float t1 = yTrace - y0;
const float t0 = 1.0f - t1;
const float u1 = zTrace - z0;
const float u0 = 1.0f - u1;
const int i000 = x0 + y0 * xres + z0 * slabSize;
const int i010 = x0 + y1 * xres + z0 * slabSize;
const int i100 = x1 + y0 * xres + z0 * slabSize;
const int i110 = x1 + y1 * xres + z0 * slabSize;
const int i001 = x0 + y0 * xres + z1 * slabSize;
const int i011 = x0 + y1 * xres + z1 * slabSize;
const int i101 = x1 + y0 * xres + z1 * slabSize;
const int i111 = x1 + y1 * xres + z1 * slabSize;
// interpolate
// (indices could be computed once)
newField[index] = u0 * (s0 * (t0 * oldField[i000] +
t1 * oldField[i010]) +
s1 * (t0 * oldField[i100] +
t1 * oldField[i110])) +
u1 * (s0 * (t0 * oldField[i001] +
t1 * oldField[i011]) +
s1 * (t0 * oldField[i101] +
t1 * oldField[i111]));
}
}
/////////////////////////////////////////////////////////////////////
// advect field with the maccormack method
//
// comments are the pseudocode from selle's paper
//////////////////////////////////////////////////////////////////////
void FLUID_3D::advectFieldMacCormack1(const float dt, const float* xVelocity, const float* yVelocity, const float* zVelocity,
float* oldField, float* tempResult, Vec3Int res, int zBegin, int zEnd)
{
/*const int sx= res[0];
const int sy= res[1];
const int sz= res[2];
for (int x = 0; x < sx * sy * sz; x++)
phiHatN[x] = phiHatN1[x] = oldField[x];*/ // not needed as all the values are written first
float*& phiN = oldField;
float*& phiN1 = tempResult;
// phiHatN1 = A(phiN)
advectFieldSemiLagrange( dt, xVelocity, yVelocity, zVelocity, phiN, phiN1, res, zBegin, zEnd); // uses wide data from old field and velocities (both are whole)
}
void FLUID_3D::advectFieldMacCormack2(const float dt, const float* xVelocity, const float* yVelocity, const float* zVelocity,
float* oldField, float* newField, float* tempResult, float* temp1, Vec3Int res, const unsigned char* obstacles, int zBegin, int zEnd)
{
float* phiHatN = tempResult;
float* t1 = temp1;
const int sx= res[0];
const int sy= res[1];
float*& phiN = oldField;
float*& phiN1 = newField;
// phiHatN = A^R(phiHatN1)
advectFieldSemiLagrange( -1.0f*dt, xVelocity, yVelocity, zVelocity, phiHatN, t1, res, zBegin, zEnd); // uses wide data from old field and velocities (both are whole)
// phiN1 = phiHatN1 + (phiN - phiHatN) / 2
const int border = 0;
for (int z = zBegin+border; z < zEnd-border; z++)
for (int y = border; y < sy-border; y++)
for (int x = border; x < sx-border; x++) {
int index = x + y * sx + z * sx*sy;
phiN1[index] = phiHatN[index] + (phiN[index] - t1[index]) * 0.50f;
//phiN1[index] = phiHatN1[index]; // debug, correction off
}
copyBorderX(phiN1, res, zBegin, zEnd);
copyBorderY(phiN1, res, zBegin, zEnd);
copyBorderZ(phiN1, res, zBegin, zEnd);
// clamp any newly created extrema
clampExtrema(dt, xVelocity, yVelocity, zVelocity, oldField, newField, res, zBegin, zEnd); // uses wide data from old field and velocities (both are whole)
// if the error estimate was bad, revert to first order
clampOutsideRays(dt, xVelocity, yVelocity, zVelocity, oldField, newField, res, obstacles, phiHatN, zBegin, zEnd); // phiHatN is only used at cells within thread range, so its ok
}
//////////////////////////////////////////////////////////////////////
// Clamp the extrema generated by the BFECC error correction
//////////////////////////////////////////////////////////////////////
void FLUID_3D::clampExtrema(const float dt, const float* velx, const float* vely, const float* velz,
float* oldField, float* newField, Vec3Int res, int zBegin, int zEnd)
{
const int xres= res[0];
const int yres= res[1];
const int zres= res[2];
const int slabSize = res[0] * res[1];
int bb=0;
int bt=0;
if (zBegin == 0) {bb = 1;}
if (zEnd == res[2]) {bt = 1;}
for (int z = zBegin+bb; z < zEnd-bt; z++)
for (int y = 1; y < yres-1; y++)
for (int x = 1; x < xres-1; x++)
{
const int index = x + y * xres+ z * xres*yres;
// backtrace
float xTrace = x - dt * velx[index];
float yTrace = y - dt * vely[index];
float zTrace = z - dt * velz[index];
// clamp backtrace to grid boundaries
if (xTrace < 0.5f) xTrace = 0.5f;
if (xTrace > xres - 1.5f) xTrace = xres - 1.5f;
if (yTrace < 0.5f) yTrace = 0.5f;
if (yTrace > yres - 1.5f) yTrace = yres - 1.5f;
if (zTrace < 0.5f) zTrace = 0.5f;
if (zTrace > zres - 1.5f) zTrace = zres - 1.5f;
// locate neighbors to interpolate
const int x0 = (int)xTrace;
const int x1 = x0 + 1;
const int y0 = (int)yTrace;
const int y1 = y0 + 1;
const int z0 = (int)zTrace;
const int z1 = z0 + 1;
const int i000 = x0 + y0 * xres + z0 * slabSize;
const int i010 = x0 + y1 * xres + z0 * slabSize;
const int i100 = x1 + y0 * xres + z0 * slabSize;
const int i110 = x1 + y1 * xres + z0 * slabSize;
const int i001 = x0 + y0 * xres + z1 * slabSize;
const int i011 = x0 + y1 * xres + z1 * slabSize;
const int i101 = x1 + y0 * xres + z1 * slabSize;
const int i111 = x1 + y1 * xres + z1 * slabSize;
float minField = oldField[i000];
float maxField = oldField[i000];
minField = (oldField[i010] < minField) ? oldField[i010] : minField;
maxField = (oldField[i010] > maxField) ? oldField[i010] : maxField;
minField = (oldField[i100] < minField) ? oldField[i100] : minField;
maxField = (oldField[i100] > maxField) ? oldField[i100] : maxField;
minField = (oldField[i110] < minField) ? oldField[i110] : minField;
maxField = (oldField[i110] > maxField) ? oldField[i110] : maxField;
minField = (oldField[i001] < minField) ? oldField[i001] : minField;
maxField = (oldField[i001] > maxField) ? oldField[i001] : maxField;
minField = (oldField[i011] < minField) ? oldField[i011] : minField;
maxField = (oldField[i011] > maxField) ? oldField[i011] : maxField;
minField = (oldField[i101] < minField) ? oldField[i101] : minField;
maxField = (oldField[i101] > maxField) ? oldField[i101] : maxField;
minField = (oldField[i111] < minField) ? oldField[i111] : minField;
maxField = (oldField[i111] > maxField) ? oldField[i111] : maxField;
newField[index] = (newField[index] > maxField) ? maxField : newField[index];
newField[index] = (newField[index] < minField) ? minField : newField[index];
}
}
//////////////////////////////////////////////////////////////////////
// Reverts any backtraces that go into boundaries back to first
// order -- in this case the error correction term was totally
// incorrect
//////////////////////////////////////////////////////////////////////
void FLUID_3D::clampOutsideRays(const float dt, const float* velx, const float* vely, const float* velz,
float* oldField, float* newField, Vec3Int res, const unsigned char* obstacles, const float *oldAdvection, int zBegin, int zEnd)
{
const int sx= res[0];
const int sy= res[1];
const int sz= res[2];
const int slabSize = res[0] * res[1];
int bb=0;
int bt=0;
if (zBegin == 0) {bb = 1;}
if (zEnd == res[2]) {bt = 1;}
for (int z = zBegin+bb; z < zEnd-bt; z++)
for (int y = 1; y < sy-1; y++)
for (int x = 1; x < sx-1; x++)
{
const int index = x + y * sx+ z * slabSize;
// backtrace
float xBackward = x + dt * velx[index];
float yBackward = y + dt * vely[index];
float zBackward = z + dt * velz[index];
float xTrace = x - dt * velx[index];
float yTrace = y - dt * vely[index];
float zTrace = z - dt * velz[index];
// see if it goes outside the boundaries
bool hasObstacle =
(zTrace < 1.0f) || (zTrace > sz - 2.0f) ||
(yTrace < 1.0f) || (yTrace > sy - 2.0f) ||
(xTrace < 1.0f) || (xTrace > sx - 2.0f) ||
(zBackward < 1.0f) || (zBackward > sz - 2.0f) ||
(yBackward < 1.0f) || (yBackward > sy - 2.0f) ||
(xBackward < 1.0f) || (xBackward > sx - 2.0f);
// reuse old advection instead of doing another one...
if(hasObstacle) { newField[index] = oldAdvection[index]; continue; }
// clamp to prevent an out of bounds access when looking into
// the _obstacles array
zTrace = (zTrace < 0.5f) ? 0.5f : zTrace;
zTrace = (zTrace > sz - 1.5f) ? sz - 1.5f : zTrace;
yTrace = (yTrace < 0.5f) ? 0.5f : yTrace;
yTrace = (yTrace > sy - 1.5f) ? sy - 1.5f : yTrace;
xTrace = (xTrace < 0.5f) ? 0.5f : xTrace;
xTrace = (xTrace > sx - 1.5f) ? sx - 1.5f : xTrace;
// locate neighbors to interpolate,
// do backward first since we will use the forward indices if a
// reversion is actually necessary
zBackward = (zBackward < 0.5f) ? 0.5f : zBackward;
zBackward = (zBackward > sz - 1.5f) ? sz - 1.5f : zBackward;
yBackward = (yBackward < 0.5f) ? 0.5f : yBackward;
yBackward = (yBackward > sy - 1.5f) ? sy - 1.5f : yBackward;
xBackward = (xBackward < 0.5f) ? 0.5f : xBackward;
xBackward = (xBackward > sx - 1.5f) ? sx - 1.5f : xBackward;
int x0 = (int)xBackward;
int x1 = x0 + 1;
int y0 = (int)yBackward;
int y1 = y0 + 1;
int z0 = (int)zBackward;
int z1 = z0 + 1;
if(obstacles && !hasObstacle) {
hasObstacle = hasObstacle ||
obstacles[x0 + y0 * sx + z0*slabSize] ||
obstacles[x0 + y1 * sx + z0*slabSize] ||
obstacles[x1 + y0 * sx + z0*slabSize] ||
obstacles[x1 + y1 * sx + z0*slabSize] ||
obstacles[x0 + y0 * sx + z1*slabSize] ||
obstacles[x0 + y1 * sx + z1*slabSize] ||
obstacles[x1 + y0 * sx + z1*slabSize] ||
obstacles[x1 + y1 * sx + z1*slabSize] ;
}
// reuse old advection instead of doing another one...
if(hasObstacle) { newField[index] = oldAdvection[index]; continue; }
x0 = (int)xTrace;
x1 = x0 + 1;
y0 = (int)yTrace;
y1 = y0 + 1;
z0 = (int)zTrace;
z1 = z0 + 1;
if(obstacles && !hasObstacle) {
hasObstacle = hasObstacle ||
obstacles[x0 + y0 * sx + z0*slabSize] ||
obstacles[x0 + y1 * sx + z0*slabSize] ||
obstacles[x1 + y0 * sx + z0*slabSize] ||
obstacles[x1 + y1 * sx + z0*slabSize] ||
obstacles[x0 + y0 * sx + z1*slabSize] ||
obstacles[x0 + y1 * sx + z1*slabSize] ||
obstacles[x1 + y0 * sx + z1*slabSize] ||
obstacles[x1 + y1 * sx + z1*slabSize] ;
} // obstacle array
// reuse old advection instead of doing another one...
if(hasObstacle) { newField[index] = oldAdvection[index]; continue; }
// see if either the forward or backward ray went into
// a boundary
if (hasObstacle) {
// get interpolation weights
float s1 = xTrace - x0;
float s0 = 1.0f - s1;
float t1 = yTrace - y0;
float t0 = 1.0f - t1;
float u1 = zTrace - z0;
float u0 = 1.0f - u1;
const int i000 = x0 + y0 * sx + z0 * slabSize;
const int i010 = x0 + y1 * sx + z0 * slabSize;
const int i100 = x1 + y0 * sx + z0 * slabSize;
const int i110 = x1 + y1 * sx + z0 * slabSize;
const int i001 = x0 + y0 * sx + z1 * slabSize;
const int i011 = x0 + y1 * sx + z1 * slabSize;
const int i101 = x1 + y0 * sx + z1 * slabSize;
const int i111 = x1 + y1 * sx + z1 * slabSize;
// interpolate, (indices could be computed once)
newField[index] = u0 * (s0 * (
t0 * oldField[i000] +
t1 * oldField[i010]) +
s1 * (t0 * oldField[i100] +
t1 * oldField[i110])) +
u1 * (s0 * (t0 * oldField[i001] +
t1 * oldField[i011]) +
s1 * (t0 * oldField[i101] +
t1 * oldField[i111]));
}
} // xyz
}