Includes preprocessed Mantaflow source files for both OpenMP and TBB (if OpenMP is not present, TBB files will be used instead). These files come directly from the Mantaflow repository. Future updates to the core fluid solver will take place by updating the files. Reviewed By: sergey, mont29 Maniphest Tasks: T59995 Differential Revision: https://developer.blender.org/D3850
313 lines
8.0 KiB
C++
313 lines
8.0 KiB
C++
/******************************************************************************
|
|
*
|
|
* MantaFlow fluid solver framework
|
|
* Copyright 2014 Tobias Pfaff, Nils Thuerey
|
|
*
|
|
* This program is free software, distributed under the terms of the
|
|
* Apache License, Version 2.0
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Simple image IO
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "vectorbase.h"
|
|
#include "simpleimage.h"
|
|
|
|
namespace Manta {
|
|
|
|
// write rectangle to ppm
|
|
bool SimpleImage::writePpm(
|
|
std::string filename, int minx, int miny, int maxx, int maxy, bool invertXY)
|
|
{
|
|
int w = maxx - minx;
|
|
int h = maxy - miny;
|
|
|
|
if (w <= 0 || h <= 0 || w > mSize[0] || h > mSize[1]) {
|
|
errMsg("SimpleImage::WritePPM Invalid rect: w="
|
|
<< w << ", h=" << h << ", size=" << mSize[0] << "," << mSize[1] << " min/max: " << minx
|
|
<< "," << miny << " to " << maxx << "," << maxy << ", resetting... ");
|
|
minx = miny = 0;
|
|
maxx = mSize[0] - 1;
|
|
maxy = mSize[1] - 1;
|
|
w = mSize[0] - 1;
|
|
h = mSize[1] - 1;
|
|
}
|
|
|
|
FILE *fp = fopen(filename.c_str(), "wb");
|
|
if (fp == NULL) {
|
|
errMsg("SimpleImage::WritePPM Unable to open '" << filename << "' for writing");
|
|
return false;
|
|
}
|
|
fprintf(fp, "P6\n%d %d\n255\n", w, h);
|
|
|
|
int pixCnt = 0;
|
|
for (int j = maxy - 1; j >= miny; j--)
|
|
for (int i = minx; i < maxx; i++) {
|
|
unsigned char col[3];
|
|
for (int l = 0; l < 3; l++) {
|
|
float val;
|
|
if (invertXY)
|
|
val = (float)get(j, i)[l];
|
|
else
|
|
val = (float)get(i, j)[l];
|
|
|
|
val = clamp(val, (float)0., (float)1.);
|
|
col[l] = (unsigned char)(255. * val);
|
|
}
|
|
// col[1] = col[2] = col[0];
|
|
// if (fwrite(col,1,3, fp) != 3) errMsg("SimpleImage::writePpm fwrite failed");
|
|
fwrite(col, 1, 3, fp);
|
|
pixCnt++;
|
|
// fprintf(stderr,"%d %d %d \n",col[0],i,j);
|
|
}
|
|
|
|
fclose(fp);
|
|
// debMsg("WritePPM Wrote '"<<filename<<"', region="<<minx<<","<<miny<<" to
|
|
// "<<maxx<<","<<maxy<<"; "<<pixCnt, 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SimpleImage::writePpm(std::string filename)
|
|
{
|
|
return writePpm(filename, 0, 0, getSize()[0], getSize()[1]);
|
|
}
|
|
|
|
// read in a ppm file, and init the image accordingly
|
|
bool SimpleImage::initFromPpm(std::string filename)
|
|
{
|
|
// maximum length of a line of text
|
|
const int MAXLINE = 1024;
|
|
|
|
int filetype = 0;
|
|
enum { PGM, PPM }; // possible file types
|
|
|
|
FILE *fp;
|
|
char line[MAXLINE];
|
|
int size, rowsize;
|
|
|
|
// Read in file type
|
|
fp = fopen(filename.c_str(), "rb");
|
|
if (!fp) {
|
|
if (mAbortOnError)
|
|
debMsg("SimpleImage Error - unable to open file '" << filename << "' for reading", 1);
|
|
return 0;
|
|
}
|
|
|
|
// 1st line: PPM or PGM
|
|
if (fgets(line, MAXLINE, fp) == NULL) {
|
|
if (mAbortOnError)
|
|
debMsg("SimpleImage::initFromPpm fgets failed", 1);
|
|
return 0;
|
|
}
|
|
|
|
if (line[1] == '5')
|
|
filetype = PGM;
|
|
else if (line[1] == '6')
|
|
filetype = PPM;
|
|
else {
|
|
if (mAbortOnError)
|
|
debMsg("SimpleImage Error: need PPM or PGM file as input!", 1);
|
|
return 0;
|
|
}
|
|
|
|
// Read in width and height, & allocate space
|
|
// 2nd line: width height
|
|
if (fgets(line, MAXLINE, fp) == NULL) {
|
|
if (mAbortOnError)
|
|
errMsg("SimpleImage::initFromPpm fgets failed");
|
|
return 0;
|
|
}
|
|
int windW = 0, windH = 0; // size of the window on the screen
|
|
int intsFound = sscanf(line, "%d %d", &windW, &windH);
|
|
if (intsFound == 1) {
|
|
// only X found, search on next line as well for Y...
|
|
if (sscanf(line, "%d", &windH) != 1) {
|
|
if (mAbortOnError)
|
|
errMsg("initFromPpm Ppm dimensions not found!" << windW << "," << windH);
|
|
return 0;
|
|
}
|
|
else {
|
|
// ok, found 2 lines
|
|
// debMsg("initFromPpm Ppm dimensions found!"<<windW<<","<<windH, 1);
|
|
}
|
|
}
|
|
else if (intsFound == 2) {
|
|
// ok!
|
|
}
|
|
else {
|
|
if (mAbortOnError)
|
|
errMsg("initFromPpm Ppm dimensions not found at all!" << windW << "," << windH);
|
|
return 0;
|
|
}
|
|
|
|
if (filetype == PGM) {
|
|
size = windH * windW; // greymap: 1 byte per pixel
|
|
rowsize = windW;
|
|
}
|
|
else {
|
|
// filetype == PPM
|
|
size = windH * windW * 3; // pixmap: 3 bytes per pixel
|
|
rowsize = windW * 3;
|
|
}
|
|
|
|
unsigned char *pic = new unsigned char[size]; // (GLubyte *)malloc (size);
|
|
|
|
// Read in maximum value (ignore) , could be scanned with sscanf as well, but this should be
|
|
// 255... 3rd line
|
|
if (fgets(line, MAXLINE, fp) == NULL) {
|
|
if (mAbortOnError)
|
|
errMsg("SimpleImage::initFromPpm fgets failed");
|
|
return 0;
|
|
}
|
|
|
|
// Read in the pixel array row-by-row: 1st row = top scanline */
|
|
unsigned char *ptr = NULL;
|
|
ptr = &pic[(windH - 1) * rowsize];
|
|
for (int i = windH; i > 0; i--) {
|
|
assertMsg(fread((void *)ptr, 1, rowsize, fp) == rowsize,
|
|
"SimpleImage::initFromPpm couldn't read data");
|
|
ptr -= rowsize;
|
|
}
|
|
|
|
// init image
|
|
this->init(windW, windH);
|
|
if (filetype == PGM) {
|
|
// grayscale
|
|
for (int i = 0; i < windW; i++) {
|
|
for (int j = 0; j < windH; j++) {
|
|
double r = (double)pic[(j * windW + i) * 1 + 0] / 255.;
|
|
(*this)(i, j) = Vec3(r, r, r);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// convert grid to RGB vec's
|
|
for (int i = 0; i < windW; i++) {
|
|
for (int j = 0; j < windH; j++) {
|
|
// return mpData[y*mSize[0]+x];
|
|
double r = (double)pic[(j * windW + i) * 3 + 0] / 255.;
|
|
double g = (double)pic[(j * windW + i) * 3 + 1] / 255.;
|
|
double b = (double)pic[(j * windW + i) * 3 + 2] / 255.;
|
|
|
|
//(*this)(i,j) = Vec3(r,g,b);
|
|
|
|
// RGB values have to be rotated to get the right colors!?
|
|
// this might also be an artifact of photoshop export...?
|
|
(*this)(i, j) = Vec3(g, b, r);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete[] pic;
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
// check index is valid
|
|
bool SimpleImage::indexIsValid(int i, int j)
|
|
{
|
|
if (i < 0)
|
|
return false;
|
|
if (j < 0)
|
|
return false;
|
|
if (i >= mSize[0])
|
|
return false;
|
|
if (j >= mSize[1])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
}; // namespace Manta
|
|
|
|
//*****************************************************************************
|
|
|
|
#include "grid.h"
|
|
namespace Manta {
|
|
|
|
// simple shaded output , note requires grid functionality!
|
|
static void gridPrecompLight(const Grid<Real> &density, Grid<Real> &L, Vec3 light = Vec3(1, 1, 1))
|
|
{
|
|
FOR_IJK(density)
|
|
{
|
|
Vec3 n = getGradient(density, i, j, k) * -1.;
|
|
normalize(n);
|
|
|
|
Real d = dot(light, n);
|
|
L(i, j, k) = d;
|
|
}
|
|
}
|
|
|
|
// simple shading with pre-computed gradient
|
|
static inline void shadeCell(
|
|
Vec3 &dst, int shadeMode, Real src, Real light, int depthPos, Real depthInv)
|
|
{
|
|
switch (shadeMode) {
|
|
|
|
case 1: {
|
|
// surfaces
|
|
Vec3 ambient = Vec3(0.1, 0.1, 0.1);
|
|
Vec3 diffuse = Vec3(0.9, 0.9, 0.9);
|
|
Real alpha = src;
|
|
|
|
// different color for depth?
|
|
diffuse[0] *= ((Real)depthPos * depthInv) * 0.7 + 0.3;
|
|
diffuse[1] *= ((Real)depthPos * depthInv) * 0.7 + 0.3;
|
|
|
|
Vec3 col = ambient + diffuse * light;
|
|
|
|
// img( 0+i, j ) = (1.-alpha) * img( 0+i, j ) + alpha * col;
|
|
dst = (1. - alpha) * dst + alpha * col;
|
|
} break;
|
|
|
|
default: {
|
|
// volumetrics / smoke
|
|
dst += depthInv * Vec3(src, src, src);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
//! helper to project a grid intro an image (used for ppm export and GUI displauy)
|
|
void projectImg(SimpleImage &img, const Grid<Real> &val, int shadeMode = 0, Real scale = 1.)
|
|
{
|
|
Vec3i s = val.getSize();
|
|
Vec3 si = Vec3(1. / (Real)s[0], 1. / (Real)s[1], 1. / (Real)s[2]);
|
|
|
|
// init image size
|
|
int imgSx = s[0];
|
|
if (val.is3D())
|
|
imgSx += s[2] + s[0]; // mult views in 3D
|
|
img.init(imgSx, std::max(s[0], std::max(s[1], s[2])));
|
|
|
|
// precompute lighting
|
|
Grid<Real> L(val);
|
|
gridPrecompLight(val, L, Vec3(1, 1, 1));
|
|
|
|
FOR_IJK(val)
|
|
{
|
|
Vec3i idx(i, j, k);
|
|
shadeCell(img(0 + i, j), shadeMode, val(idx), L(idx), k, si[2]);
|
|
}
|
|
|
|
if (val.is3D()) {
|
|
|
|
FOR_IJK(val)
|
|
{
|
|
Vec3i idx(i, j, k);
|
|
shadeCell(img(s[0] + k, j), shadeMode, val(idx), L(idx), i, si[0]);
|
|
}
|
|
|
|
FOR_IJK(val)
|
|
{
|
|
Vec3i idx(i, j, k);
|
|
shadeCell(img(s[0] + s[2] + i, k), shadeMode, val(idx), L(idx), j, si[1]);
|
|
}
|
|
|
|
} // 3d
|
|
|
|
img.mapRange(1. / scale);
|
|
}
|
|
|
|
}; // namespace Manta
|