mitsuba/src/libcore/util.cpp

1104 lines
28 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba 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/>.
*/
#include <mitsuba/mitsuba.h>
#include <mitsuba/core/util.h>
#include <mitsuba/core/random.h>
#include <mitsuba/core/quad.h>
#include <mitsuba/core/sse.h>
#include <boost/bind.hpp>
#include <stdarg.h>
#include <iomanip>
#include <errno.h>
#if defined(__OSX__)
#include <sys/sysctl.h>
#elif defined(WIN32)
#include <direct.h>
#else
#include <malloc.h>
#endif
#if defined(WIN32)
# include <windows.h>
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
# include <fenv.h>
#endif
MTS_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
// General utility functions
// -----------------------------------------------------------------------
std::vector<std::string> tokenize(const std::string &string, const std::string &delim) {
std::string::size_type lastPos = string.find_first_not_of(delim, 0);
std::string::size_type pos = string.find_first_of(delim, lastPos);
std::vector<std::string> tokens;
while (std::string::npos != pos || std::string::npos != lastPos) {
tokens.push_back(string.substr(lastPos, pos - lastPos));
lastPos = string.find_first_not_of(delim, pos);
pos = string.find_first_of(delim, lastPos);
}
return tokens;
}
std::string trim(const std::string& str) {
std::string::size_type
start = str.find_first_not_of(" \t\r\n"),
end = str.find_last_not_of(" \t\r\n");
return str.substr(start == std::string::npos ? 0 : start,
end == std::string::npos ? str.length() - 1 : end - start + 1);
}
std::string indent(const std::string &string, int amount) {
/* This could probably be done faster (is not
really speed-critical though) */
std::istringstream iss(string);
std::ostringstream oss;
std::string str;
bool firstLine = true;
while (!iss.eof()) {
std::getline(iss, str);
if (!firstLine) {
for (int i=0; i<amount; ++i)
oss << " ";
}
oss << str;
if (!iss.eof())
oss << endl;
firstLine = false;
}
return oss.str();
}
std::string memString(size_t size) {
Float value = (Float) size;
const char *prefixes[] = {
"B", "KiB", "MiB", "GiB", "TiB", "PiB"
};
int prefix = 0;
while (prefix < 5 && value > 1024.0f) {
value /= 1024.0f; ++prefix;
}
return formatString(prefix == 0 ?
"%.0f %s" : "%.2f %s", value, prefixes[prefix]);
}
void * __restrict allocAligned(size_t size) {
#if defined(WIN32)
return _aligned_malloc(size, L1_CACHE_LINE_SIZE);
#elif defined(__OSX__)
/* OSX malloc already returns 16-byte aligned data suitable
for AltiVec and SSE computations */
return malloc(size);
#else
return memalign(L1_CACHE_LINE_SIZE, size);
#endif
}
void freeAligned(void *ptr) {
#if defined(WIN32)
_aligned_free(ptr);
#else
free(ptr);
#endif
}
int getCoreCount() {
#if defined(WIN32)
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
return sys_info.dwNumberOfProcessors;
#elif defined(__OSX__)
int nprocs;
size_t nprocsSize = sizeof(int);
if (sysctlbyname("hw.activecpu", &nprocs, &nprocsSize, NULL, 0))
SLog(EError, "Could not detect the number of processors!");
return (int) nprocs;
#else
return sysconf(_SC_NPROCESSORS_CONF);
#endif
}
#if defined(WIN32)
std::string lastErrorText() {
DWORD errCode = GetLastError();
char *errorText = NULL;
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &errorText,
0,
NULL)) {
return "Internal error while looking up an error code";
}
std::string result(errorText);
LocalFree(errorText);
return result;
}
#endif
bool enableFPExceptions() {
bool exceptionsWereEnabled = false;
#if defined(WIN32)
_clearfp();
uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
cw &= ~(_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
_controlfp(cw, _MCW_EM);
#elif defined(__OSX__)
#if !defined(MTS_SSE)
#warning SSE must be enabled to handle FP exceptions on OSX
#else
exceptionsWereEnabled = query_fpexcept_sse() != 0;
#endif
#else
exceptionsWereEnabled =
fegetexcept() & (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
feenableexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
#endif
#if defined(MTS_SSE)
enable_fpexcept_sse();
#endif
return exceptionsWereEnabled;
}
bool disableFPExceptions() {
bool exceptionsWereEnabled = false;
#if defined(WIN32)
_clearfp();
uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
cw |= _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW;
_controlfp(cw, _MCW_EM);
#elif defined(__OSX__)
#if !defined(MTS_SSE)
#warning SSE must be enabled to handle FP exceptions on OSX
#else
exceptionsWereEnabled = query_fpexcept_sse() != 0;
#endif
#else
exceptionsWereEnabled =
fegetexcept() & (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
fedisableexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
#endif
#if defined(MTS_SSE)
disable_fpexcept_sse();
#endif
return exceptionsWereEnabled;
}
void restoreFPExceptions(bool oldState) {
bool currentState;
#if defined(WIN32)
uint32_t cw = _controlfp(0, 0);
currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
#elif defined(__OSX__)
#if !defined(MTS_SSE)
#warning SSE must be enabled to handle FP exceptions on OSX
#else
currentState = query_fpexcept_sse() != 0;
#endif
#else
currentState = fegetexcept() & (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
#endif
if (oldState != currentState) {
if (oldState)
enableFPExceptions();
else
disableFPExceptions();
}
}
std::string getHostName() {
char hostName[128];
if (gethostname(hostName, sizeof(hostName)) != 0)
#if defined(WIN32)
SLog(EError, "Could not retrieve the computer's host name: %s!",
lastErrorText().c_str());
#else
SLog(EError, "Could not retrieve the computer's host name : %s!",
strerror(errno));
#endif
return hostName;
}
std::string getFQDN() {
struct addrinfo *addrInfo = NULL, hints;
memset(&hints, 0, sizeof(addrinfo));
// Only look for IPv4 addresses -- perhaps revisit this later
hints.ai_family = AF_INET; // AF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int retVal = getaddrinfo(getHostName().c_str(), NULL, &hints, &addrInfo);
if (addrInfo == NULL || retVal != 0) {
SLog(EWarn, "Could not retrieve the computer's fully "
"qualified domain name: could not resolve host address \"%s\"!",
getHostName().c_str());
return getHostName();
}
char fqdn[NI_MAXHOST];
retVal = getnameinfo(addrInfo->ai_addr, sizeof(struct sockaddr_in),
fqdn, NI_MAXHOST, NULL, 0, 0);
if (retVal != 0) {
freeaddrinfo(addrInfo);
#if defined(WIN32)
SLog(EWarn, "Could not retrieve the computer's fully "
"qualified domain name: error %i!", WSAGetLastError());
#else
SLog(EWarn, "Could not retrieve the computer's fully "
"qualified domain name: error %i!", gai_strerror(retVal));
#endif
return getHostName();
}
freeaddrinfo(addrInfo);
return fqdn;
}
Float log2(Float value) {
const Float invLn2 = (Float) 1.0f / math::fastlog((Float) 2.0f);
return math::fastlog(value) * invLn2;
}
std::string formatString(const char *fmt, ...) {
char tmp[512];
va_list iterator;
#if defined(WIN32)
va_start(iterator, fmt);
size_t size = _vscprintf(fmt, iterator) + 1;
if (size >= sizeof(tmp)) {
char *dest = new char[size];
vsnprintf_s(dest, size, size-1, fmt, iterator);
va_end(iterator);
std::string result(dest);
delete[] dest;
return result;
}
vsnprintf_s(tmp, size, size-1, fmt, iterator);
va_end(iterator);
#else
va_start(iterator, fmt);
size_t size = vsnprintf(tmp, sizeof(tmp), fmt, iterator);
va_end(iterator);
if (size >= sizeof(tmp)) {
/* Overflow! -- dynamically allocate memory */
char *dest = new char[size+1];
va_start(iterator, fmt);
vsnprintf(dest, size+1, fmt, iterator);
va_end(iterator);
std::string result(dest);
delete[] dest;
return result;
}
#endif
return std::string(tmp);
}
int log2i(uint32_t value) {
int r = 0;
while ((value >> r) != 0)
r++;
return r-1;
}
int log2i(uint64_t value) {
int r = 0;
while ((value >> r) != 0)
r++;
return r-1;
}
/* Fast rounding & power-of-two test algorithms from PBRT */
uint32_t roundToPowerOfTwo(uint32_t i) {
i--;
i |= i >> 1; i |= i >> 2;
i |= i >> 4; i |= i >> 8;
i |= i >> 16;
return i+1;
}
uint64_t roundToPowerOfTwo(uint64_t i) {
i--;
i |= i >> 1; i |= i >> 2;
i |= i >> 4; i |= i >> 8;
i |= i >> 16; i |= i >> 32;
return i+1;
}
// -----------------------------------------------------------------------
// Numerical utility functions
// -----------------------------------------------------------------------
bool solveQuadratic(Float a, Float b, Float c, Float &x0, Float &x1) {
/* Linear case */
if (a == 0) {
if (b != 0) {
x0 = x1 = -c / b;
return true;
}
return false;
}
Float discrim = b*b - 4.0f*a*c;
/* Leave if there is no solution */
if (discrim < 0)
return false;
Float temp, sqrtDiscrim = std::sqrt(discrim);
/* Numerically stable version of (-b (+/-) sqrtDiscrim) / (2 * a)
*
* Based on the observation that one solution is always
* accurate while the other is not. Finds the solution of
* greater magnitude which does not suffer from loss of
* precision and then uses the identity x1 * x2 = c / a
*/
if (b < 0)
temp = -0.5f * (b - sqrtDiscrim);
else
temp = -0.5f * (b + sqrtDiscrim);
x0 = temp / a;
x1 = c / temp;
/* Return the results so that x0 < x1 */
if (x0 > x1)
std::swap(x0, x1);
return true;
}
bool solveQuadraticDouble(double a, double b, double c, double &x0, double &x1) {
/* Linear case */
if (a == 0) {
if (b != 0) {
x0 = x1 = -c / b;
return true;
}
return false;
}
double discrim = b*b - 4.0f*a*c;
/* Leave if there is no solution */
if (discrim < 0)
return false;
double temp, sqrtDiscrim = std::sqrt(discrim);
/* Numerically stable version of (-b (+/-) sqrtDiscrim) / (2 * a)
*
* Based on the observation that one solution is always
* accurate while the other is not. Finds the solution of
* greater magnitude which does not suffer from loss of
* precision and then uses the identity x1 * x2 = c / a
*/
if (b < 0)
temp = -0.5f * (b - sqrtDiscrim);
else
temp = -0.5f * (b + sqrtDiscrim);
x0 = temp / a;
x1 = c / temp;
/* Return the results so that x0 < x1 */
if (x0 > x1)
std::swap(x0, x1);
return true;
}
bool solveLinearSystem2x2(const Float a[2][2], const Float b[2], Float x[2]) {
Float det = a[0][0] * a[1][1] - a[0][1] * a[1][0];
if (det == 0)
return false;
Float inverse = (Float) 1.0f / det;
x[0] = (a[1][1] * b[0] - a[0][1] * b[1]) * inverse;
x[1] = (a[0][0] * b[1] - a[1][0] * b[0]) * inverse;
return true;
}
Float interpCubic1D(Float x, const Float *data, Float min, Float max, size_t size) {
/* Give up when given an out-of-range or NaN argument */
if (!(x >= min && x <= max))
return 0.0f;
/* Transform 'x' so that knots lie at integer positions */
Float t = ((x - min) * (size - 1)) / (max - min);
/* Find the index of the left knot in the queried subinterval, be
robust to cases where 't' lies exactly on the right endpoint */
size_t k = std::max((size_t) 0, std::min((size_t) t, size - 2));
Float f0 = data[k],
f1 = data[k+1],
d0, d1;
/* Approximate the derivatives */
if (k > 0)
d0 = 0.5f * (data[k+1] - data[k-1]);
else
d0 = data[k+1] - data[k];
if (k + 2 < size)
d1 = 0.5f * (data[k+2] - data[k]);
else
d1 = data[k+1] - data[k];
/* Compute the relative position within the interval */
t = t - (Float) k;
Float t2 = t*t, t3 = t2*t;
return
( 2*t3 - 3*t2 + 1) * f0 +
(-2*t3 + 3*t2) * f1 +
( t3 - 2*t2 + t) * d0 +
( t3 - t2) * d1;
}
Float interpCubic1DIrregular(Float x, const Float *nodes, const Float *data, size_t size) {
/* Give up when given an out-of-range or NaN argument */
if (!(x >= nodes[0] && x <= nodes[size-1]))
return 0.0f;
size_t k = (size_t) std::max((ptrdiff_t) 0, std::min((ptrdiff_t) size - 2,
std::lower_bound(nodes, nodes + size, x) - nodes - 1));
Float f0 = data[k],
f1 = data[k+1],
width = nodes[k+1] - nodes[k],
invWidth = 1.0f / width,
d0, d1;
/* Approximate the derivatives */
if (k > 0)
d0 = (f1 - data[k-1]) / (nodes[k+1] - nodes[k-1]);
else
d0 = (f1 - f0) * invWidth;
if (k + 2 < size)
d1 = (data[k+2] - f0) / (nodes[k+2] - nodes[k]);
else
d1 = (f1 - f0) * invWidth;
Float t = (x - nodes[k]) * invWidth;
Float t2 = t*t, t3 = t2*t;
return
( 2*t3 - 3*t2 + 1) * f0 +
(-2*t3 + 3*t2) * f1 +
(( t3 - 2*t2 + t) * d0 +
( t3 - t2) * d1) * width;
}
Float interpCubic2D(const Point2 &p, const Float *data,
const Point2 &min, const Point2 &max, const Size2 &size) {
Float knotWeights[2][4];
Size2 knot;
/* Compute interpolation weights separately for each dimension */
for (int dim=0; dim<2; ++dim) {
Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= min[dim] && p[dim] <= max[dim]))
return 0.0f;
/* Transform 'p' so that knots lie at integer positions */
Float t = ((p[dim] - min[dim]) * (size[dim] - 1))
/ (max[dim]-min[dim]);
/* Find the index of the left knot in the queried subinterval, be
robust to cases where 't' lies exactly on the right endpoint */
knot[dim] = std::min((size_t) t, size[dim] - 2);
/* Compute the relative position within the interval */
t = t - (Float) knot[dim];
/* Compute node weights */
Float t2 = t*t, t3 = t2*t;
weights[0] = 0.0f;
weights[1] = 2*t3 - 3*t2 + 1;
weights[2] = -2*t3 + 3*t2;
weights[3] = 0.0f;
/* Derivative weights */
Float d0 = t3 - 2*t2 + t,
d1 = t3 - t2;
/* Turn derivative weights into node weights using
an appropriate chosen finite differences stencil */
if (knot[dim] > 0) {
weights[2] += 0.5f * d0;
weights[0] -= 0.5f * d0;
} else {
weights[2] += d0;
weights[1] -= d0;
}
if (knot[dim] + 2 < size[dim]) {
weights[3] += 0.5f * d1;
weights[1] -= 0.5f * d1;
} else {
weights[2] += d1;
weights[1] -= d1;
}
}
Float result = 0.0f;
for (int y=-1; y<=2; ++y) {
Float wy = knotWeights[1][y+1];
for (int x=-1; x<=2; ++x) {
Float wxy = knotWeights[0][x+1] * wy;
if (wxy == 0)
continue;
size_t pos = (knot[1] + y) * size[0] + knot[0] + x;
result += data[pos] * wxy;
}
}
return result;
}
Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_,
const Float *data, const Size2 &size) {
Float knotWeights[2][4];
Size2 knot;
/* Compute interpolation weights separately for each dimension */
for (int dim=0; dim<2; ++dim) {
const Float *nodes = nodes_[dim];
Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1]))
return 0.0f;
/* Find the index of the left knot in the queried subinterval, be
robust to cases where 't' lies exactly on the right endpoint */
size_t k = (size_t) std::max((ptrdiff_t) 0, std::min((ptrdiff_t) size[dim] - 2,
std::lower_bound(nodes, nodes + size[dim], p[dim]) - nodes - 1));
knot[dim] = k;
Float width = nodes[k+1] - nodes[k], invWidth = 1 / width;
/* Compute the relative position within the interval */
Float t = (p[dim] - nodes[k]) * invWidth,
t2 = t*t, t3 = t2*t;
/* Compute node weights */
weights[0] = 0.0f;
weights[1] = 2*t3 - 3*t2 + 1;
weights[2] = -2*t3 + 3*t2;
weights[3] = 0.0f;
/* Derivative weights */
Float d0 = (t3 - 2*t2 + t) * width,
d1 = (t3 - t2) * width;
/* Turn derivative weights into node weights using
an appropriate chosen finite differences stencil */
if (k > 0) {
Float factor = 1 / (nodes[k+1]-nodes[k-1]);
weights[2] += d0 * factor;
weights[0] -= d0 * factor;
} else {
weights[2] += d0 * invWidth;
weights[1] -= d0 * invWidth;
}
if (k + 2 < size[dim]) {
Float factor = 1 / (nodes[k+2]-nodes[k]);
weights[3] += d1 * factor;
weights[1] -= d1 * factor;
} else {
weights[2] += d1 * invWidth;
weights[1] -= d1 * invWidth;
}
}
Float result = 0.0f;
for (int y=-1; y<=2; ++y) {
Float wy = knotWeights[1][y+1];
for (int x=-1; x<=2; ++x) {
Float wxy = knotWeights[0][x+1] * wy;
if (wxy == 0)
continue;
size_t pos = (knot[1] + y) * size[0] + knot[0] + x;
result += data[pos] * wxy;
}
}
return result;
}
Float interpCubic3D(const Point3 &p, const Float *data,
const Point3 &min, const Point3 &max, const Size3 &size) {
Float knotWeights[3][4];
Size3 knot;
/* Compute interpolation weights separately for each dimension */
for (int dim=0; dim<3; ++dim) {
Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= min[dim] && p[dim] <= max[dim]))
return 0.0f;
/* Transform 'p' so that knots lie at integer positions */
Float t = ((p[dim] - min[dim]) * (size[dim] - 1))
/ (max[dim]-min[dim]);
/* Find the index of the left knot in the queried subinterval, be
robust to cases where 't' lies exactly on the right endpoint */
knot[dim] = std::min((size_t) t, size[dim] - 2);
/* Compute the relative position within the interval */
t = t - (Float) knot[dim];
/* Compute node weights */
Float t2 = t*t, t3 = t2*t;
weights[0] = 0.0f;
weights[1] = 2*t3 - 3*t2 + 1;
weights[2] = -2*t3 + 3*t2;
weights[3] = 0.0f;
/* Derivative weights */
Float d0 = t3 - 2*t2 + t,
d1 = t3 - t2;
/* Turn derivative weights into node weights using
an appropriate chosen finite differences stencil */
if (knot[dim] > 0) {
weights[2] += 0.5f * d0;
weights[0] -= 0.5f * d0;
} else {
weights[2] += d0;
weights[1] -= d0;
}
if (knot[dim] + 2 < size[dim]) {
weights[3] += 0.5f * d1;
weights[1] -= 0.5f * d1;
} else {
weights[2] += d1;
weights[1] -= d1;
}
}
Float result = 0.0f;
for (int z=-1; z<=2; ++z) {
Float wz = knotWeights[2][z+1];
for (int y=-1; y<=2; ++y) {
Float wyz = knotWeights[1][y+1] * wz;
for (int x=-1; x<=2; ++x) {
Float wxyz = knotWeights[0][x+1] * wyz;
if (wxyz == 0)
continue;
size_t pos = ((knot[2] + z) * size[1] + (knot[1] + y))
* size[0] + knot[0] + x;
result += data[pos] * wxyz;
}
}
}
return result;
}
Float interpCubic3DIrregular(const Point3 &p, const Float **nodes_,
const Float *data, const Size3 &size) {
Float knotWeights[3][4];
Size3 knot;
/* Compute interpolation weights separately for each dimension */
for (int dim=0; dim<3; ++dim) {
const Float *nodes = nodes_[dim];
Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1]))
return 0.0f;
/* Find the index of the left knot in the queried subinterval, be
robust to cases where 't' lies exactly on the right endpoint */
size_t k = (size_t) std::max((ptrdiff_t) 0, std::min((ptrdiff_t) size[dim] - 2,
std::lower_bound(nodes, nodes + size[dim], p[dim]) - nodes - 1));
knot[dim] = k;
Float width = nodes[k+1] - nodes[k], invWidth = 1 / width;
/* Compute the relative position within the interval */
Float t = (p[dim] - nodes[k]) * invWidth,
t2 = t*t, t3 = t2*t;
/* Compute node weights */
weights[0] = 0.0f;
weights[1] = 2*t3 - 3*t2 + 1;
weights[2] = -2*t3 + 3*t2;
weights[3] = 0.0f;
/* Derivative weights */
Float d0 = (t3 - 2*t2 + t) * width,
d1 = (t3 - t2) * width;
/* Turn derivative weights into node weights using
an appropriate chosen finite differences stencil */
if (k > 0) {
Float factor = 1 / (nodes[k+1]-nodes[k-1]);
weights[2] += d0 * factor;
weights[0] -= d0 * factor;
} else {
weights[2] += d0 * invWidth;
weights[1] -= d0 * invWidth;
}
if (k + 2 < size[dim]) {
Float factor = 1 / (nodes[k+2]-nodes[k]);
weights[3] += d1 * factor;
weights[1] -= d1 * factor;
} else {
weights[2] += d1 * invWidth;
weights[1] -= d1 * invWidth;
}
}
Float result = 0.0f;
for (int z=-1; z<=2; ++z) {
Float wz = knotWeights[2][z+1];
for (int y=-1; y<=2; ++y) {
Float wyz = knotWeights[1][y+1] * wz;
for (int x=-1; x<=2; ++x) {
Float wxyz = knotWeights[0][x+1] * wyz;
if (wxyz == 0)
continue;
size_t pos = ((knot[2] + z) * size[1] + (knot[1] + y))
* size[0] + knot[0] + x;
result += data[pos] * wxyz;
}
}
}
return result;
}
void stratifiedSample1D(Random *random, Float *dest, int count, bool jitter) {
Float invCount = 1.0f / count;
for (int i=0; i<count; i++) {
Float offset = jitter ? random->nextFloat() : 0.5f;
*dest++ = (i + offset) * invCount;
}
}
void stratifiedSample2D(Random *random, Point2 *dest, int countX, int countY, bool jitter) {
Float invCountX = 1.0f / countX;
Float invCountY = 1.0f / countY;
for (int x=0; x<countX; x++) {
for (int y=0; y<countY; y++) {
Float offsetX = jitter ? random->nextFloat() : 0.5f;
Float offsetY = jitter ? random->nextFloat() : 0.5f;
*dest++ = Point2(
(x + offsetX) * invCountX,
(y + offsetY) * invCountY
);
}
}
}
void latinHypercube(Random *random, Float *dest, size_t nSamples, size_t nDim) {
Float delta = 1 / (Float) nSamples;
for (size_t i = 0; i < nSamples; ++i)
for (size_t j = 0; j < nDim; ++j)
dest[nDim * i + j] = (i + random->nextFloat()) * delta;
for (size_t i = 0; i < nDim; ++i) {
for (size_t j = 0; j < nSamples; ++j) {
size_t other = random->nextSize(nSamples);
std::swap(dest[nDim * j + i], dest[nDim * other + i]);
}
}
}
Vector sphericalDirection(Float theta, Float phi) {
Float sinTheta, cosTheta, sinPhi, cosPhi;
math::sincos(theta, &sinTheta, &cosTheta);
math::sincos(phi, &sinPhi, &cosPhi);
return Vector(
sinTheta * cosPhi,
sinTheta * sinPhi,
cosTheta
);
}
void coordinateSystem(const Vector &a, Vector &b, Vector &c) {
if (std::abs(a.x) > std::abs(a.y)) {
Float invLen = 1.0f / std::sqrt(a.x * a.x + a.z * a.z);
c = Vector(a.z * invLen, 0.0f, -a.x * invLen);
} else {
Float invLen = 1.0f / std::sqrt(a.y * a.y + a.z * a.z);
c = Vector(0.0f, a.z * invLen, -a.y * invLen);
}
b = cross(c, a);
}
Point2 toSphericalCoordinates(const Vector &v) {
Point2 result(
std::acos(v.z),
std::atan2(v.y, v.x)
);
if (result.y < 0)
result.y += 2*M_PI;
return result;
}
Float fresnelDielectric(Float cosThetaI, Float cosThetaT, Float eta) {
if (EXPECT_NOT_TAKEN(eta == 1))
return 0.0f;
Float Rs = (cosThetaI - eta * cosThetaT)
/ (cosThetaI + eta * cosThetaT);
Float Rp = (eta * cosThetaI - cosThetaT)
/ (eta * cosThetaI + cosThetaT);
/* No polarization -- return the unpolarized reflectance */
return 0.5f * (Rs * Rs + Rp * Rp);
}
Float fresnelDielectricExt(Float cosThetaI_, Float &cosThetaT_, Float eta) {
if (EXPECT_NOT_TAKEN(eta == 1)) {
cosThetaT_ = -cosThetaI_;
return 0.0f;
}
/* Using Snell's law, calculate the squared sine of the
angle between the normal and the transmitted ray */
Float scale = (cosThetaI_ > 0) ? 1/eta : eta,
cosThetaTSqr = 1 - (1-cosThetaI_*cosThetaI_) * (scale*scale);
/* Check for total internal reflection */
if (cosThetaTSqr <= 0.0f) {
cosThetaT_ = 0.0f;
return 1.0f;
}
/* Find the absolute cosines of the incident/transmitted rays */
Float cosThetaI = std::abs(cosThetaI_);
Float cosThetaT = std::sqrt(cosThetaTSqr);
Float Rs = (cosThetaI - eta * cosThetaT)
/ (cosThetaI + eta * cosThetaT);
Float Rp = (eta * cosThetaI - cosThetaT)
/ (eta * cosThetaI + cosThetaT);
cosThetaT_ = (cosThetaI_ > 0) ? -cosThetaT : cosThetaT;
/* No polarization -- return the unpolarized reflectance */
return 0.5f * (Rs * Rs + Rp * Rp);
}
Spectrum fresnelConductor(Float cosThetaI, const Spectrum &eta, const Spectrum &k) {
Spectrum tmp = (eta*eta + k*k) * (cosThetaI * cosThetaI);
Spectrum rParl2 = (tmp - (eta * (2.0f * cosThetaI)) + Spectrum(1.0f))
/ (tmp + (eta * (2.0f * cosThetaI)) + Spectrum(1.0f));
Spectrum tmpF = eta*eta + k*k;
Spectrum rPerp2 = (tmpF - (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI)) /
(tmpF + (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI));
return (rParl2 + rPerp2) / 2.0f;
}
Vector reflect(const Vector &wi, const Normal &n) {
return 2 * dot(wi, n) * Vector(n) - wi;
}
Vector refract(const Vector &wi, const Normal &n, Float eta, Float cosThetaT) {
if (cosThetaT < 0)
eta = 1 / eta;
return n * (dot(wi, n) * eta + cosThetaT) - wi * eta;
}
Vector refract(const Vector &wi, const Normal &n, Float eta) {
if (EXPECT_NOT_TAKEN(eta == 1))
return -wi;
Float cosThetaI = dot(wi, n);
if (cosThetaI > 0)
eta = 1 / eta;
/* Using Snell's law, calculate the squared sine of the
angle between the normal and the transmitted ray */
Float cosThetaTSqr = 1 - (1-cosThetaI*cosThetaI) * (eta*eta);
/* Check for total internal reflection */
if (cosThetaTSqr <= 0.0f)
return Vector(0.0f);
return n * (cosThetaI * eta - math::signum(cosThetaI)
* std::sqrt(cosThetaTSqr)) - wi * eta;
}
Vector refract(const Vector &wi, const Normal &n, Float eta, Float &cosThetaT, Float &F) {
Float cosThetaI = dot(wi, n);
F = fresnelDielectricExt(cosThetaI, cosThetaT, eta);
if (F == 1.0f) /* Total internal reflection */
return Vector(0.0f);
if (cosThetaT < 0)
eta = 1 / eta;
return n * (eta * cosThetaI + cosThetaT) - wi * eta;
}
namespace {
/// Integrand used by fresnelDiffuseReflectance
inline Float fresnelDiffuseIntegrand(Float eta, Float xi) {
return fresnelDielectricExt(std::sqrt(xi), eta);
}
};
Float fresnelDiffuseReflectance(Float eta, bool fast) {
if (fast) {
/* Fast mode: the following code approximates the
* diffuse Frensel reflectance for the eta<1 and
* eta>1 cases. An evalution of the accuracy led
* to the following scheme, which cherry-picks
* fits from two papers where they are best.
*/
if (eta < 1) {
/* Fit by Egan and Hilgeman (1973). Works
reasonably well for "normal" IOR values (<2).
Max rel. error in 1.0 - 1.5 : 0.1%
Max rel. error in 1.5 - 2 : 0.6%
Max rel. error in 2.0 - 5 : 9.5%
*/
return -1.4399f * (eta * eta)
+ 0.7099f * eta
+ 0.6681f
+ 0.0636f / eta;
} else {
/* Fit by d'Eon and Irving (2011)
*
* Maintains a good accuracy even for
* unrealistic IOR values.
*
* Max rel. error in 1.0 - 2.0 : 0.1%
* Max rel. error in 2.0 - 10.0 : 0.2%
*/
Float invEta = 1.0f / eta,
invEta2 = invEta*invEta,
invEta3 = invEta2*invEta,
invEta4 = invEta3*invEta,
invEta5 = invEta4*invEta;
return 0.919317f - 3.4793f * invEta
+ 6.75335f * invEta2
- 7.80989f * invEta3
+ 4.98554f * invEta4
- 1.36881f * invEta5;
}
} else {
GaussLobattoIntegrator quad(1024, 0, 1e-5f);
return quad.integrate(
boost::bind(&fresnelDiffuseIntegrand, eta, _1), 0, 1);
}
return 0.0f;
}
std::string timeString(Float time, bool precise) {
std::ostringstream os;
if (std::isnan(time) || std::isinf(time))
return "inf";
char suffix = 's';
if (time > 60) {
time /= 60; suffix = 'm';
if (time > 60) {
time /= 60; suffix = 'h';
if (time > 12) {
time /= 12; suffix = 'd';
}
}
}
os << std::setprecision(precise ? 4 : 1)
<< std::fixed << time << suffix;
return os.str();
}
Float hypot2(Float a, Float b) {
Float r;
if (std::abs(a) > std::abs(b)) {
r = b / a;
r = std::abs(a) * std::sqrt(1 + r*r);
} else if (b != 0) {
r = a / b;
r = std::abs(b) * std::sqrt(1 + r*r);
} else {
r = 0;
}
return r;
}
MTS_NAMESPACE_END