Nori  24
common.cpp
1 /*
2  This file is part of Nori, a simple educational ray tracer
3 
4  Copyright (c) 2015 by Wenzel Jakob, Romain Prévost
5 
6  Nori is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License Version 3
8  as published by the Free Software Foundation.
9 
10  Nori is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <nori/object.h>
20 #include <Eigen/Geometry>
21 #include <Eigen/LU>
22 #include <filesystem/resolver.h>
23 #include <iomanip>
24 
25 #if defined(PLATFORM_LINUX)
26 #include <malloc.h>
27 #endif
28 
29 #if defined(PLATFORM_WINDOWS)
30 #include <windows.h>
31 #endif
32 
33 #if defined(PLATFORM_MACOS)
34 #include <sys/sysctl.h>
35 #endif
36 
37 NORI_NAMESPACE_BEGIN
38 
39 std::string indent(const std::string &string, int amount) {
40  /* This could probably be done faster (it's not
41  really speed-critical though) */
42  std::istringstream iss(string);
43  std::ostringstream oss;
44  std::string spacer(amount, ' ');
45  bool firstLine = true;
46  for (std::string line; std::getline(iss, line); ) {
47  if (!firstLine)
48  oss << spacer;
49  oss << line;
50  if (!iss.eof())
51  oss << endl;
52  firstLine = false;
53  }
54  return oss.str();
55 }
56 
57 bool endsWith(const std::string &value, const std::string &ending) {
58  if (ending.size() > value.size())
59  return false;
60  return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
61 }
62 
63 std::string toLower(const std::string &value) {
64  std::string result;
65  result.resize(value.size());
66  std::transform(value.begin(), value.end(), result.begin(), ::tolower);
67  return result;
68 }
69 
70 bool toBool(const std::string &str) {
71  std::string value = toLower(str);
72  if (value == "false")
73  return false;
74  else if (value == "true")
75  return true;
76  else
77  throw NoriException("Could not parse boolean value \"%s\"", str);
78 }
79 
80 int toInt(const std::string &str) {
81  char *end_ptr = nullptr;
82  int result = (int) strtol(str.c_str(), &end_ptr, 10);
83  if (*end_ptr != '\0')
84  throw NoriException("Could not parse integer value \"%s\"", str);
85  return result;
86 }
87 
88 unsigned int toUInt(const std::string &str) {
89  char *end_ptr = nullptr;
90  unsigned int result = (int) strtoul(str.c_str(), &end_ptr, 10);
91  if (*end_ptr != '\0')
92  throw NoriException("Could not parse integer value \"%s\"", str);
93  return result;
94 }
95 
96 float toFloat(const std::string &str) {
97  char *end_ptr = nullptr;
98  float result = (float) strtof(str.c_str(), &end_ptr);
99  if (*end_ptr != '\0')
100  throw NoriException("Could not parse floating point value \"%s\"", str);
101  return result;
102 }
103 
104 size_t vectorSize(const std::string &str) {
105  std::vector<std::string> tokens = tokenize(str);
106  return tokens.size();
107 }
108 
109 Eigen::Vector2f toVector2f(const std::string &str) {
110  std::vector<std::string> tokens = tokenize(str);
111  if (tokens.size() != 2)
112  throw NoriException("Expected 2 values");
113  Eigen::Vector2f result;
114  for (int i=0; i<2; ++i)
115  result[i] = toFloat(tokens[i]);
116  return result;
117 }
118 Eigen::Vector3f toVector3f(const std::string &str) {
119  std::vector<std::string> tokens = tokenize(str);
120  if (tokens.size() != 3)
121  throw NoriException("Expected 3 values");
122  Eigen::Vector3f result;
123  for (int i=0; i<3; ++i)
124  result[i] = toFloat(tokens[i]);
125  return result;
126 }
127 
128 std::vector<std::string> tokenize(const std::string &string, const std::string &delim, bool includeEmpty) {
129  std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos);
130  std::vector<std::string> tokens;
131 
132  while (lastPos != std::string::npos) {
133  if (pos != lastPos || includeEmpty)
134  tokens.push_back(string.substr(lastPos, pos - lastPos));
135  lastPos = pos;
136  if (lastPos != std::string::npos) {
137  lastPos += 1;
138  pos = string.find_first_of(delim, lastPos);
139  }
140  }
141 
142  return tokens;
143 }
144 
145 std::string timeString(double time, bool precise) {
146  if (std::isnan(time) || std::isinf(time))
147  return "inf";
148 
149  std::string suffix = "ms";
150  if (time > 1000) {
151  time /= 1000; suffix = "s";
152  if (time > 60) {
153  time /= 60; suffix = "m";
154  if (time > 60) {
155  time /= 60; suffix = "h";
156  if (time > 12) {
157  time /= 12; suffix = "d";
158  }
159  }
160  }
161  }
162 
163  std::ostringstream os;
164  os << std::setprecision(precise ? 4 : 1)
165  << std::fixed << time << suffix;
166 
167  return os.str();
168 }
169 
170 std::string memString(size_t size, bool precise) {
171  double value = (double) size;
172  const char *suffixes[] = {
173  "B", "KiB", "MiB", "GiB", "TiB", "PiB"
174  };
175  int suffix = 0;
176  while (suffix < 5 && value > 1024.0f) {
177  value /= 1024.0f; ++suffix;
178  }
179 
180  std::ostringstream os;
181  os << std::setprecision(suffix == 0 ? 0 : (precise ? 4 : 1))
182  << std::fixed << value << " " << suffixes[suffix];
183 
184  return os.str();
185 }
186 
187 filesystem::resolver *getFileResolver() {
188  static filesystem::resolver *resolver = new filesystem::resolver();
189  return resolver;
190 }
191 
193  Color3f result;
194 
195  for (int i=0; i<3; ++i) {
196  float value = coeff(i);
197 
198  if (value <= 0.0031308f)
199  result[i] = 12.92f * value;
200  else
201  result[i] = (1.0f + 0.055f)
202  * std::pow(value, 1.0f/2.4f) - 0.055f;
203  }
204 
205  return result;
206 }
207 
209  Color3f result;
210 
211  for (int i=0; i<3; ++i) {
212  float value = coeff(i);
213 
214  if (value <= 0.04045f)
215  result[i] = value * (1.0f / 12.92f);
216  else
217  result[i] = std::pow((value + 0.055f)
218  * (1.0f / 1.055f), 2.4f);
219  }
220 
221  return result;
222 }
223 
224 bool Color3f::isValid() const {
225  for (int i=0; i<3; ++i) {
226  float value = coeff(i);
227  if (value < 0 || !std::isfinite(value))
228  return false;
229  }
230  return true;
231 }
232 
233 float Color3f::getLuminance() const {
234  return coeff(0) * 0.212671f + coeff(1) * 0.715160f + coeff(2) * 0.072169f;
235 }
236 
237 Transform::Transform(const Eigen::Matrix4f &trafo)
238  : m_transform(trafo), m_inverse(trafo.inverse()) { }
239 
240 std::string Transform::toString() const {
241  std::ostringstream oss;
242  oss << m_transform.format(Eigen::IOFormat(4, 0, ", ", ";\n", "", "", "[", "]"));
243  return oss.str();
244 }
245 
247  return Transform(m_transform * t.m_transform,
248  t.m_inverse * m_inverse);
249 }
250 
251 Vector3f sphericalDirection(float theta, float phi) {
252  float sinTheta, cosTheta, sinPhi, cosPhi;
253 
254  sincosf(theta, &sinTheta, &cosTheta);
255  sincosf(phi, &sinPhi, &cosPhi);
256 
257  return Vector3f(
258  sinTheta * cosPhi,
259  sinTheta * sinPhi,
260  cosTheta
261  );
262 }
263 
264 Point2f sphericalCoordinates(const Vector3f &v) {
265  Point2f result(
266  std::acos(v.z()),
267  std::atan2(v.y(), v.x())
268  );
269  if (result.y() < 0)
270  result.y() += 2*M_PI;
271  return result;
272 }
273 
274 void coordinateSystem(const Vector3f &a, Vector3f &b, Vector3f &c) {
275  if (std::abs(a.x()) > std::abs(a.y())) {
276  float invLen = 1.0f / std::sqrt(a.x() * a.x() + a.z() * a.z());
277  c = Vector3f(a.z() * invLen, 0.0f, -a.x() * invLen);
278  } else {
279  float invLen = 1.0f / std::sqrt(a.y() * a.y() + a.z() * a.z());
280  c = Vector3f(0.0f, a.z() * invLen, -a.y() * invLen);
281  }
282  b = c.cross(a);
283 }
284 
285 float fresnel(float cosThetaI, float extIOR, float intIOR) {
286  float etaI = extIOR, etaT = intIOR;
287 
288  if (extIOR == intIOR)
289  return 0.0f;
290 
291  /* Swap the indices of refraction if the interaction starts
292  at the inside of the object */
293  if (cosThetaI < 0.0f) {
294  std::swap(etaI, etaT);
295  cosThetaI = -cosThetaI;
296  }
297 
298  /* Using Snell's law, calculate the squared sine of the
299  angle between the normal and the transmitted ray */
300  float eta = etaI / etaT,
301  sinThetaTSqr = eta*eta * (1-cosThetaI*cosThetaI);
302 
303  if (sinThetaTSqr > 1.0f)
304  return 1.0f; /* Total internal reflection! */
305 
306  float cosThetaT = std::sqrt(1.0f - sinThetaTSqr);
307 
308  float Rs = (etaI * cosThetaI - etaT * cosThetaT)
309  / (etaI * cosThetaI + etaT * cosThetaT);
310  float Rp = (etaT * cosThetaI - etaI * cosThetaT)
311  / (etaT * cosThetaI + etaI * cosThetaT);
312 
313  return (Rs * Rs + Rp * Rp) / 2.0f;
314 }
315 
316 NORI_NAMESPACE_END
Simple exception class, which stores a human-readable error description.
Definition: common.h:148
Represents a linear RGB color value.
Definition: color.h:29
bool isValid() const
Check if the color vector contains a NaN/Inf/negative value.
Definition: common.cpp:224
float getLuminance() const
Return the associated luminance.
Definition: common.cpp:233
Color3f toLinearRGB() const
Convert from sRGB to linear RGB.
Definition: common.cpp:208
Color3f toSRGB() const
Convert from linear RGB to sRGB.
Definition: common.cpp:192
Homogeneous coordinate transformation.
Definition: transform.h:35
Transform operator*(const Transform &t) const
Concatenate with another transform.
Definition: common.cpp:246
std::string toString() const
Return a string representation.
Definition: common.cpp:240
Transform()
Create the identity transform.
Definition: transform.h:38