Merge with default
commit
ec3ade8ad2
|
@ -30,7 +30,7 @@ if needsBuildDependencies and not os.path.exists(GetBuildPath('#dependencies')):
|
||||||
print 'at http://www.mitsuba-renderer.org/docs.html for details on how to get them.\n'
|
print 'at http://www.mitsuba-renderer.org/docs.html for details on how to get them.\n'
|
||||||
Exit(1)
|
Exit(1)
|
||||||
|
|
||||||
python_versions = ["2.6", "2.7", "3.0", "3.1", "3.2"]
|
python_versions = ["2.6", "2.7", "3.0", "3.1", "3.2", "3.3"]
|
||||||
|
|
||||||
# Parse configuration options
|
# Parse configuration options
|
||||||
vars = Variables(configFile)
|
vars = Variables(configFile)
|
||||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
||||||
env = locals()
|
env = locals()
|
||||||
|
|
||||||
env['PYTHON'+pyver+'INCLUDE'] = []
|
env['PYTHON'+pyver+'INCLUDE'] = []
|
||||||
env['PYTHON'+pyver+'LIB'] = ['boost_python3' if pyver[0] == '3' else 'boost_python']
|
env['PYTHON'+pyver+'LIB'] = ['boost_python-mt-py'+pyver]
|
||||||
|
|
||||||
for entry in os.popen("python-config --cflags --libs").read().split():
|
for entry in os.popen("python-config --cflags --libs").read().split():
|
||||||
if entry[:2] == '-I':
|
if entry[:2] == '-I':
|
||||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
||||||
env = locals()
|
env = locals()
|
||||||
|
|
||||||
env['PYTHON'+pyver+'INCLUDE'] = []
|
env['PYTHON'+pyver+'INCLUDE'] = []
|
||||||
env['PYTHON'+pyver+'LIB'] = ['boost_python3' if pyver[0] == '3' else 'boost_python']
|
env['PYTHON'+pyver+'LIB'] = ['boost_python-mt-py'+pyver]
|
||||||
|
|
||||||
for entry in os.popen("python-config --cflags --libs").read().split():
|
for entry in os.popen("python-config --cflags --libs").read().split():
|
||||||
if entry[:2] == '-I':
|
if entry[:2] == '-I':
|
||||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
||||||
env = locals()
|
env = locals()
|
||||||
|
|
||||||
env['PYTHON'+pyver+'INCLUDE'] = []
|
env['PYTHON'+pyver+'INCLUDE'] = []
|
||||||
env['PYTHON'+pyver+'LIB'] = ['boost_python3' if pyver[0] == '3' else 'boost_python']
|
env['PYTHON'+pyver+'LIB'] = ['boost_python-mt-py'+pyver]
|
||||||
|
|
||||||
for entry in os.popen("python-config --cflags --libs").read().split():
|
for entry in os.popen("python-config --cflags --libs").read().split():
|
||||||
if entry[:2] == '-I':
|
if entry[:2] == '-I':
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
BUILDDIR = '#build/release'
|
||||||
|
DISTDIR = '#Mitsuba.app'
|
||||||
|
CXX = 'clang++'
|
||||||
|
CC = 'clang'
|
||||||
|
CCFLAGS = ['-arch', 'x86_64', '-mmacosx-version-min=10.8', '-march=nocona', '-msse2', '-ftree-vectorize', '-funsafe-math-optimizations', '-fno-math-errno', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk', '-O3', '-Wall', '-g', '-pipe', '-DMTS_DEBUG', '-DSINGLE_PRECISION', '-DSPECTRUM_SAMPLES=3', '-DMTS_SSE', '-DMTS_HAS_COHERENT_RT', '-fstrict-aliasing', '-fsched-interblock', '-freorder-blocks', '-fvisibility=hidden', '-ftemplate-depth=512']
|
||||||
|
LINKFLAGS = ['-framework', 'OpenGL', '-framework', 'Cocoa', '-arch', 'x86_64', '-mmacosx-version-min=10.8', '-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk', '-Wl,-headerpad,128']
|
||||||
|
BASEINCLUDE = ['#include', '#dependencies/include']
|
||||||
|
BASELIBDIR = ['#dependencies/lib']
|
||||||
|
BASELIB = ['m', 'pthread', 'Half']
|
||||||
|
OEXRINCLUDE = ['#dependencies/include/OpenEXR']
|
||||||
|
OEXRLIB = ['IlmImf', 'Imath', 'Iex', 'z']
|
||||||
|
PNGLIB = ['png']
|
||||||
|
JPEGLIB = ['jpeg']
|
||||||
|
XERCESLIB = ['xerces-c']
|
||||||
|
GLLIB = ['GLEWmx', 'objc']
|
||||||
|
GLFLAGS = ['-DGLEW_MX']
|
||||||
|
BOOSTINCLUDE = ['#dependencies']
|
||||||
|
BOOSTLIB = ['boost_filesystem', 'boost_system', 'boost_thread']
|
||||||
|
PYTHON26INCLUDE= ['/System/Library/Frameworks/Python.framework/Versions/2.6/Headers']
|
||||||
|
PYTHON26LIBDIR = ['/System/Library/Frameworks/Python.framework/Versions/2.6/lib']
|
||||||
|
PYTHON26LIB = ['boost_python26', 'boost_system', 'python2.6']
|
||||||
|
PYTHON27INCLUDE= ['/System/Library/Frameworks/Python.framework/Versions/2.7/Headers']
|
||||||
|
PYTHON27LIBDIR = ['/System/Library/Frameworks/Python.framework/Versions/2.7/lib']
|
||||||
|
PYTHON27LIB = ['boost_python27', 'boost_system', 'python2.7']
|
||||||
|
COLLADAINCLUDE = ['#dependencies/include/collada-dom', '#dependencies/include/collada-dom/1.4']
|
||||||
|
COLLADALIB = ['collada14dom23']
|
||||||
|
QTDIR = '#dependencies'
|
|
@ -137,6 +137,7 @@
|
||||||
<xsd:element name="emitter" type="emitter"/>
|
<xsd:element name="emitter" type="emitter"/>
|
||||||
<xsd:element name="shape" type="shape"/>
|
<xsd:element name="shape" type="shape"/>
|
||||||
<xsd:element name="medium" type="medium"/>
|
<xsd:element name="medium" type="medium"/>
|
||||||
|
<xsd:element name="texture" type="texture"/>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
</xsd:extension>
|
</xsd:extension>
|
||||||
</xsd:complexContent>
|
</xsd:complexContent>
|
||||||
|
|
|
@ -153,7 +153,7 @@ scheduler = Scheduler.getInstance()
|
||||||
|
|
||||||
# Start up the scheduling system with one worker per local core
|
# Start up the scheduling system with one worker per local core
|
||||||
for i in range(0, multiprocessing.cpu_count()):
|
for i in range(0, multiprocessing.cpu_count()):
|
||||||
scheduler.registerWorker(LocalWorker('wrk%i' % i))
|
scheduler.registerWorker(LocalWorker(i, 'wrk%i' % i))
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|
||||||
# Create a queue for tracking render jobs
|
# Create a queue for tracking render jobs
|
||||||
|
|
|
@ -292,6 +292,11 @@ template <typename T> struct TAABB {
|
||||||
/** \brief Calculate the near and far ray-AABB intersection
|
/** \brief Calculate the near and far ray-AABB intersection
|
||||||
* points (if they exist).
|
* points (if they exist).
|
||||||
*
|
*
|
||||||
|
* The parameters \c nearT and \c farT are used to return the
|
||||||
|
* ray distances to the intersections (including negative distances).
|
||||||
|
* Any previously contained value is overwritten, even if there was
|
||||||
|
* no intersection.
|
||||||
|
*
|
||||||
* \remark In the Python bindings, this function returns the
|
* \remark In the Python bindings, this function returns the
|
||||||
* \c nearT and \c farT values as a tuple (or \c None, when no
|
* \c nearT and \c farT values as a tuple (or \c None, when no
|
||||||
* intersection was found)
|
* intersection was found)
|
||||||
|
@ -302,11 +307,10 @@ template <typename T> struct TAABB {
|
||||||
|
|
||||||
/* For each pair of AABB planes */
|
/* For each pair of AABB planes */
|
||||||
for (int i=0; i<PointType::dim; i++) {
|
for (int i=0; i<PointType::dim; i++) {
|
||||||
const Float direction = ray.d[i];
|
|
||||||
const Float origin = ray.o[i];
|
const Float origin = ray.o[i];
|
||||||
const Float minVal = min[i], maxVal = max[i];
|
const Float minVal = min[i], maxVal = max[i];
|
||||||
|
|
||||||
if (direction == 0) {
|
if (ray.d[i] == 0) {
|
||||||
/* The ray is parallel to the planes */
|
/* The ray is parallel to the planes */
|
||||||
if (origin < minVal || origin > maxVal)
|
if (origin < minVal || origin > maxVal)
|
||||||
return false;
|
return false;
|
||||||
|
@ -329,6 +333,68 @@ template <typename T> struct TAABB {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Calculate the overlap between an axis-aligned bounding box
|
||||||
|
* and a ray segment
|
||||||
|
*
|
||||||
|
* This function is an extended version of the simpler \ref rayIntersect command
|
||||||
|
* provided above. The first change is that input values passed via
|
||||||
|
* the \c nearT and \c farT parameters are considered to specify a query interval.
|
||||||
|
*
|
||||||
|
* This interval is intersected against the bounding box, returning the remaining
|
||||||
|
* interval using the \c nearT and \c farT parameters. Furthermore, the
|
||||||
|
* interval endpoints are also returned as 3D positions via the \c near and
|
||||||
|
* \c far parameters. Special care is taken to reduce round-off errors.
|
||||||
|
*
|
||||||
|
* \remark Not currently exposed via the Python bindings
|
||||||
|
*/
|
||||||
|
FINLINE bool rayIntersect(const Ray &ray, Float &nearT, Float &farT, Point &near, Point &far) const {
|
||||||
|
int nearAxis = -1, farAxis = -1;
|
||||||
|
|
||||||
|
/* For each pair of AABB planes */
|
||||||
|
for (int i=0; i<PointType::dim; i++) {
|
||||||
|
const Float origin = ray.o[i];
|
||||||
|
const Float minVal = min[i], maxVal = max[i];
|
||||||
|
|
||||||
|
if (ray.d[i] == 0) {
|
||||||
|
/* The ray is parallel to the planes */
|
||||||
|
if (origin < minVal || origin > maxVal)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
/* Calculate intersection distances */
|
||||||
|
Float t1 = (minVal - origin) * ray.dRcp[i];
|
||||||
|
Float t2 = (maxVal - origin) * ray.dRcp[i];
|
||||||
|
|
||||||
|
bool flip = t1 > t2;
|
||||||
|
if (flip)
|
||||||
|
std::swap(t1, t2);
|
||||||
|
|
||||||
|
if (t1 > nearT) {
|
||||||
|
nearT = t1;
|
||||||
|
nearAxis = flip ? (i + PointType::dim) : i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t2 < farT) {
|
||||||
|
farT = t2;
|
||||||
|
farAxis = flip ? i : (i + PointType::dim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(nearT <= farT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
near = ray(nearT); far = ray(farT);
|
||||||
|
|
||||||
|
/* Avoid roundoff errors on the component where the intersection took place */
|
||||||
|
if (nearAxis >= 0)
|
||||||
|
near[nearAxis % PointType::dim] = ((Float *) this)[nearAxis];
|
||||||
|
|
||||||
|
if (farAxis >= 0)
|
||||||
|
far[farAxis % PointType::dim] = ((Float *) this)[farAxis];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Serialize this bounding box to a binary data stream
|
/// Serialize this bounding box to a binary data stream
|
||||||
inline void serialize(Stream *stream) const {
|
inline void serialize(Stream *stream) const {
|
||||||
min.serialize(stream);
|
min.serialize(stream);
|
||||||
|
|
|
@ -270,6 +270,14 @@ public:
|
||||||
ERotate90FlipY = ERotate270FlipX
|
ERotate90FlipY = ERotate270FlipX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The four basic arithmetic operations for use with \ref arithmeticOperation()
|
||||||
|
enum EArithmeticOperation {
|
||||||
|
EAddition = 0,
|
||||||
|
ESubtraction,
|
||||||
|
EMultiplication,
|
||||||
|
EDivision
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Create a bitmap of the specified type and allocate
|
* \brief Create a bitmap of the specified type and allocate
|
||||||
* the necessary amount of memory
|
* the necessary amount of memory
|
||||||
|
@ -287,9 +295,13 @@ public:
|
||||||
* \param channelCount
|
* \param channelCount
|
||||||
* Channel count of the image. This parameter is only required when
|
* Channel count of the image. This parameter is only required when
|
||||||
* \c pFmt = \ref EMultiChannel
|
* \c pFmt = \ref EMultiChannel
|
||||||
|
*
|
||||||
|
* \param data
|
||||||
|
* External pointer to the image data. If set to \c NULL, the
|
||||||
|
* implementation will allocate memory itself.
|
||||||
*/
|
*/
|
||||||
Bitmap(EPixelFormat pFmt, EComponentFormat cFmt, const Vector2i &size,
|
Bitmap(EPixelFormat pFmt, EComponentFormat cFmt, const Vector2i &size,
|
||||||
int channelCount = -1);
|
uint8_t channelCount = 0, uint8_t *data = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Load a bitmap from an arbitrary stream data source
|
* \brief Load a bitmap from an arbitrary stream data source
|
||||||
|
@ -335,7 +347,7 @@ public:
|
||||||
inline int getHeight() const { return m_size.y; }
|
inline int getHeight() const { return m_size.y; }
|
||||||
|
|
||||||
/// Return the number of channels used by this bitmap
|
/// Return the number of channels used by this bitmap
|
||||||
inline int getChannelCount() const { return m_channelCount; }
|
inline int getChannelCount() const { return (int) m_channelCount; }
|
||||||
|
|
||||||
/// Return whether this image has matching width and height
|
/// Return whether this image has matching width and height
|
||||||
inline bool isSquare() const { return m_size.x == m_size.y; }
|
inline bool isSquare() const { return m_size.x == m_size.y; }
|
||||||
|
@ -712,18 +724,13 @@ public:
|
||||||
ref<Bitmap> rotateFlip(ERotateFlipType type) const;
|
ref<Bitmap> rotateFlip(ERotateFlipType type) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Accumulate the contents of another bitmap into the
|
* \brief Scale the entire image by a certain value
|
||||||
* region of the specified offset
|
|
||||||
*
|
*
|
||||||
* Out-of-bounds regions are ignored. It is assumed that
|
* Skips the image's alpha channel, if it has one. When the image uses
|
||||||
* <tt>bitmap != this</tt>.
|
* a fixed point representation and a pixel value overflows during the
|
||||||
*
|
* scale operation, it is clamped to the representable range.
|
||||||
* \remark This function throws an exception when the bitmaps
|
|
||||||
* use different component formats or channels, or when the
|
|
||||||
* component format is \ref EBitmask.
|
|
||||||
*/
|
*/
|
||||||
void accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
void scale(Float value);
|
||||||
Point2i targetOffset, Vector2i size);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Color balancing: apply the given scale factors to the
|
* \brief Color balancing: apply the given scale factors to the
|
||||||
|
@ -743,6 +750,19 @@ public:
|
||||||
*/
|
*/
|
||||||
void applyMatrix(Float matrix[3][3]);
|
void applyMatrix(Float matrix[3][3]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Accumulate the contents of another bitmap into the
|
||||||
|
* region of the specified offset
|
||||||
|
*
|
||||||
|
* Out-of-bounds regions are ignored. It is assumed that
|
||||||
|
* <tt>bitmap != this</tt>.
|
||||||
|
*
|
||||||
|
* \remark This function throws an exception when the bitmaps
|
||||||
|
* use different component formats or channels, or when the
|
||||||
|
* component format is \ref EBitmask.
|
||||||
|
*/
|
||||||
|
void accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
||||||
|
Point2i targetOffset, Vector2i size);
|
||||||
/**
|
/**
|
||||||
* \brief Accumulate the contents of another bitmap into the
|
* \brief Accumulate the contents of another bitmap into the
|
||||||
* region of the specified offset
|
* region of the specified offset
|
||||||
|
@ -760,6 +780,41 @@ public:
|
||||||
accumulate(bitmap, Point2i(0), targetOffset, bitmap->getSize());
|
accumulate(bitmap, Point2i(0), targetOffset, bitmap->getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Accumulate the contents of another bitmap into the
|
||||||
|
* region of the specified offset
|
||||||
|
*
|
||||||
|
* This convenience function calls the main <tt>accumulate()</tt>
|
||||||
|
* implementation with <tt>size</tt> set to <tt>bitmap->getSize()</tt>
|
||||||
|
* and <tt>sourceOffset</tt> and <tt>targetOffset</tt>tt> set to zero.
|
||||||
|
* Out-of-bounds regions are ignored. It is assumed
|
||||||
|
* that <tt>bitmap != this</tt>.
|
||||||
|
*
|
||||||
|
* \remark This function throws an exception when the bitmaps
|
||||||
|
* use different component formats or channels, or when the
|
||||||
|
* component format is \ref EBitmask.
|
||||||
|
*/
|
||||||
|
inline void accumulate(const Bitmap *bitmap) {
|
||||||
|
accumulate(bitmap, Point2i(0), Point2i(0), bitmap->getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Perform an arithmetic operation using two images
|
||||||
|
*
|
||||||
|
* This function can add, subtract, multiply, or divide arbitrary
|
||||||
|
* images. If the input images have different sizes or component
|
||||||
|
* and pixel formats, the implementation first resamples and
|
||||||
|
* converts them into the most "expressive" format that subsumes
|
||||||
|
* both input images (at the cost of some temporary dynamic
|
||||||
|
* memory allocations).
|
||||||
|
*
|
||||||
|
* To keep the implementation simple, there is currently no
|
||||||
|
* special treatment of integer over/underflows if the component
|
||||||
|
* format is \ref EUInt8, \ref EUInt16, or \ref EUInt32.
|
||||||
|
*/
|
||||||
|
static ref<Bitmap> arithmeticOperation(EArithmeticOperation operation,
|
||||||
|
const Bitmap *bitmap1, const Bitmap *bitmap2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Up- or down-sample this image to a different resolution
|
* \brief Up- or down-sample this image to a different resolution
|
||||||
*
|
*
|
||||||
|
@ -941,7 +996,8 @@ protected:
|
||||||
Vector2i m_size;
|
Vector2i m_size;
|
||||||
uint8_t *m_data;
|
uint8_t *m_data;
|
||||||
Float m_gamma;
|
Float m_gamma;
|
||||||
int m_channelCount;
|
uint8_t m_channelCount;
|
||||||
|
bool m_ownsData;
|
||||||
Properties m_metadata;
|
Properties m_metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,7 @@ typedef TVector2<size_t> Size2;
|
||||||
typedef TVector3<size_t> Size3;
|
typedef TVector3<size_t> Size3;
|
||||||
typedef TVector4<size_t> Size4;
|
typedef TVector4<size_t> Size4;
|
||||||
typedef TAABB<Point1> AABB1;
|
typedef TAABB<Point1> AABB1;
|
||||||
|
typedef AABB1 Interval;
|
||||||
typedef TAABB<Point2> AABB2;
|
typedef TAABB<Point2> AABB2;
|
||||||
typedef TAABB<Point4> AABB4;
|
typedef TAABB<Point4> AABB4;
|
||||||
/// \ingroup libpython
|
/// \ingroup libpython
|
||||||
|
|
|
@ -173,6 +173,9 @@ template <typename T> struct TPoint1 {
|
||||||
stream->writeElement<T>(x);
|
stream->writeElement<T>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implicit conversion to Scalar
|
||||||
|
operator Scalar() const { return x; }
|
||||||
|
|
||||||
/// Return a readable string representation of this point
|
/// Return a readable string representation of this point
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
|
@ -98,7 +98,7 @@ template <typename _PointType, typename _VectorType> struct TRay {
|
||||||
/// Set the origin
|
/// Set the origin
|
||||||
inline void setOrigin(const PointType &pos) { o = pos; }
|
inline void setOrigin(const PointType &pos) { o = pos; }
|
||||||
|
|
||||||
/// Set the origin
|
/// Set the time
|
||||||
inline void setTime(Scalar tval) { time = tval; }
|
inline void setTime(Scalar tval) { time = tval; }
|
||||||
|
|
||||||
/// Set the direction and update the reciprocal
|
/// Set the direction and update the reciprocal
|
||||||
|
|
|
@ -758,7 +758,7 @@ protected:
|
||||||
*/
|
*/
|
||||||
class MTS_EXPORT_CORE LocalWorker : public Worker {
|
class MTS_EXPORT_CORE LocalWorker : public Worker {
|
||||||
public:
|
public:
|
||||||
LocalWorker(const std::string &name,
|
LocalWorker(int coreID, const std::string &name,
|
||||||
Thread::EThreadPriority priority = Thread::ENormalPriority);
|
Thread::EThreadPriority priority = Thread::ENormalPriority);
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
|
|
@ -156,7 +156,7 @@ extern MTS_EXPORT_CORE Float integrateCubicInterp1DN(size_t idx,
|
||||||
* \return
|
* \return
|
||||||
* The sampled position
|
* The sampled position
|
||||||
*/
|
*/
|
||||||
extern MTS_EXPORT_CORE Float sampleCubicInterp1D(size_t idx, Float *values,
|
extern MTS_EXPORT_CORE Float sampleCubicInterp1D(size_t idx, const Float *values,
|
||||||
size_t size, Float min, Float max, Float sample, Float *fval = NULL);
|
size_t size, Float min, Float max, Float sample, Float *fval = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,8 +182,8 @@ extern MTS_EXPORT_CORE Float sampleCubicInterp1D(size_t idx, Float *values,
|
||||||
* \return
|
* \return
|
||||||
* The sampled position
|
* The sampled position
|
||||||
*/
|
*/
|
||||||
extern MTS_EXPORT_CORE Float sampleCubicInterp1DN(size_t idx, Float *nodes,
|
extern MTS_EXPORT_CORE Float sampleCubicInterp1DN(size_t idx, const Float *nodes,
|
||||||
Float *values, size_t size, Float sample, Float *fval = NULL);
|
const Float *values, size_t size, Float sample, Float *fval = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Evaluate a cubic spline interpolant of a uniformly sampled 2D function
|
* \brief Evaluate a cubic spline interpolant of a uniformly sampled 2D function
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <mitsuba/core/timer.h>
|
#include <mitsuba/core/timer.h>
|
||||||
#include <mitsuba/core/atomic.h>
|
#include <mitsuba/core/atomic.h>
|
||||||
|
|
||||||
|
//#define MTS_NO_STATISTICS 1
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# include <intrin.h>
|
# include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -110,6 +112,7 @@ public:
|
||||||
inline uint64_t operator++() {
|
inline uint64_t operator++() {
|
||||||
#if defined(MTS_NO_STATISTICS)
|
#if defined(MTS_NO_STATISTICS)
|
||||||
// do nothing
|
// do nothing
|
||||||
|
return 0;
|
||||||
#elif defined(_MSC_VER) && defined(_WIN64)
|
#elif defined(_MSC_VER) && defined(_WIN64)
|
||||||
const int offset = Thread::getID() & NUM_COUNTERS_MASK;
|
const int offset = Thread::getID() & NUM_COUNTERS_MASK;
|
||||||
_InterlockedExchangeAdd64(reinterpret_cast<__int64 volatile *>(&m_value[offset].value), 1);
|
_InterlockedExchangeAdd64(reinterpret_cast<__int64 volatile *>(&m_value[offset].value), 1);
|
||||||
|
|
|
@ -66,6 +66,19 @@ public:
|
||||||
/// Return the thread priority
|
/// Return the thread priority
|
||||||
EThreadPriority getPriority() const;
|
EThreadPriority getPriority() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set the core affinity
|
||||||
|
*
|
||||||
|
* This function provides a hint to the operating system
|
||||||
|
* scheduler that the thread should preferably run
|
||||||
|
* on the specified processor core. By default, the parameter
|
||||||
|
* is set to -1, which means that there is no affinity.
|
||||||
|
*/
|
||||||
|
void setCoreAffinity(int core);
|
||||||
|
|
||||||
|
/// Return the core affinity
|
||||||
|
int getCoreAffinity() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Specify whether or not this thread is critical
|
* \brief Specify whether or not this thread is critical
|
||||||
*
|
*
|
||||||
|
|
|
@ -291,7 +291,18 @@ inline Float signum(Float value) {
|
||||||
return -1;
|
return -1;
|
||||||
else if (value > 0)
|
else if (value > 0)
|
||||||
return 1;
|
return 1;
|
||||||
else return 0;
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the signum (a.k.a. "sign") function, and return an integer value
|
||||||
|
inline int signumToInt(Float value) {
|
||||||
|
if (value < 0)
|
||||||
|
return -1;
|
||||||
|
else if (value > 0)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Integer floor function
|
/// Integer floor function
|
||||||
|
|
|
@ -165,6 +165,9 @@ template <typename T> struct TVector1 {
|
||||||
stream->writeElement<T>(x);
|
stream->writeElement<T>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implicit conversion to Scalar
|
||||||
|
operator Scalar() const { return x; }
|
||||||
|
|
||||||
/// Return a readable string representation of this vector
|
/// Return a readable string representation of this vector
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
|
|
||||||
Shader *createShader(Renderer *renderer) const;
|
Shader *createShader(Renderer *renderer) const;
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
@ -128,6 +130,8 @@ public:
|
||||||
|
|
||||||
Shader *createShader(Renderer *renderer) const;
|
Shader *createShader(Renderer *renderer) const;
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
@ -185,6 +189,8 @@ public:
|
||||||
|
|
||||||
Shader *createShader(Renderer *renderer) const;
|
Shader *createShader(Renderer *renderer) const;
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
@ -242,6 +248,8 @@ public:
|
||||||
|
|
||||||
Shader *createShader(Renderer *renderer) const;
|
Shader *createShader(Renderer *renderer) const;
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
@ -302,6 +310,8 @@ public:
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
ref<const Texture> m_a, m_b;
|
ref<const Texture> m_a, m_b;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(SINGLE_PRECISION)
|
#if defined(SINGLE_PRECISION)
|
||||||
/// 32 byte temporary storage for intersection computations
|
/// 64 byte temporary storage for intersection computations
|
||||||
#define MTS_KD_INTERSECTION_TEMP 64
|
#define MTS_KD_INTERSECTION_TEMP 64
|
||||||
#else
|
#else
|
||||||
#define MTS_KD_INTERSECTION_TEMP 128
|
#define MTS_KD_INTERSECTION_TEMP 128
|
||||||
|
|
|
@ -74,8 +74,17 @@ public:
|
||||||
/// Serialize to a binary data stream
|
/// Serialize to a binary data stream
|
||||||
virtual void serialize(Stream *stream, InstanceManager *manager) const;
|
virtual void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
/// Return the underlying bitmap representation (if any)
|
/**
|
||||||
virtual ref<Bitmap> getBitmap() const;
|
* \brief Return a bitmap representation of the texture
|
||||||
|
*
|
||||||
|
* When the class implementing this interface is a bitmap-backed texture,
|
||||||
|
* this function directly returns the underlying bitmap. When it is procedural,
|
||||||
|
* a bitmap version must first be generated. In this case, the parameter
|
||||||
|
* \ref sizeHint is used to control the target size. The default
|
||||||
|
* value <tt>-1, -1</tt> allows the implementation to choose a suitable
|
||||||
|
* size by itself.
|
||||||
|
*/
|
||||||
|
virtual ref<Bitmap> getBitmap(const Vector2i &sizeHint = Vector2i(-1, -1)) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
|
@ -109,6 +118,18 @@ public:
|
||||||
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
|
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
|
||||||
const Vector2 &d1) const = 0;
|
const Vector2 &d1) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return a bitmap representation of the texture
|
||||||
|
*
|
||||||
|
* When the class implementing this interface is a bitmap-backed texture,
|
||||||
|
* this function directly returns the underlying bitmap. When it is procedural,
|
||||||
|
* a bitmap version must first be generated. In this case, the parameter
|
||||||
|
* \ref sizeHint is used to control the target size. The default
|
||||||
|
* value <tt>-1, -1</tt> allows the implementation to choose a suitable
|
||||||
|
* size by itself.
|
||||||
|
*/
|
||||||
|
virtual ref<Bitmap> getBitmap(const Vector2i &sizeHint = Vector2i(-1, -1)) const;
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
Texture2D(const Properties &props);
|
Texture2D(const Properties &props);
|
||||||
|
|
|
@ -212,7 +212,10 @@ public:
|
||||||
Float mean = 0, meanSqr = 0.0f;
|
Float mean = 0, meanSqr = 0.0f;
|
||||||
sampleCount = 0;
|
sampleCount = 0;
|
||||||
|
|
||||||
while (!stop) {
|
while (true) {
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
rRec.newQuery(RadianceQueryRecord::ESensorRay, sensor->getMedium());
|
rRec.newQuery(RadianceQueryRecord::ESensorRay, sensor->getMedium());
|
||||||
rRec.extra = RadianceQueryRecord::EAdaptiveQuery;
|
rRec.extra = RadianceQueryRecord::EAdaptiveQuery;
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ Spectrum BeamRadianceEstimator::query(const Ray &r, const Medium *medium) const
|
||||||
Float diskDistance = dot(originToCenter, ray.d), radSqr = node.radius * node.radius;
|
Float diskDistance = dot(originToCenter, ray.d), radSqr = node.radius * node.radius;
|
||||||
Float distSqr = (ray(diskDistance) - node.photon.getPosition()).lengthSquared();
|
Float distSqr = (ray(diskDistance) - node.photon.getPosition()).lengthSquared();
|
||||||
|
|
||||||
if (distSqr < radSqr) {
|
if (diskDistance > 0 && distSqr < radSqr) {
|
||||||
Float weight = K2(distSqr/radSqr)/radSqr;
|
Float weight = K2(distSqr/radSqr)/radSqr;
|
||||||
|
|
||||||
Vector wi = -node.photon.getDirection();
|
Vector wi = -node.photon.getDirection();
|
||||||
|
|
|
@ -445,7 +445,7 @@ public:
|
||||||
/* Exhaustively recurse into all specular lobes? */
|
/* Exhaustively recurse into all specular lobes? */
|
||||||
bool exhaustiveSpecular = rRec.depth < m_maxSpecularDepth && !cacheQuery;
|
bool exhaustiveSpecular = rRec.depth < m_maxSpecularDepth && !cacheQuery;
|
||||||
|
|
||||||
if (isDiffuse) {
|
if (isDiffuse && (dot(its.shFrame.n, ray.d) < 0 || (bsdf->getType() & BSDF::EBackSide))) {
|
||||||
/* 1. Diffuse indirect */
|
/* 1. Diffuse indirect */
|
||||||
int maxDepth = m_maxDepth == -1 ? INT_MAX : (m_maxDepth-rRec.depth);
|
int maxDepth = m_maxDepth == -1 ? INT_MAX : (m_maxDepth-rRec.depth);
|
||||||
if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance && m_globalPhotonMap.get())
|
if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance && m_globalPhotonMap.get())
|
||||||
|
|
|
@ -48,6 +48,9 @@ bool PathEdge::sampleNext(const Scene *scene, Sampler *sampler,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!medium) {
|
if (!medium) {
|
||||||
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
||||||
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
||||||
|
@ -103,6 +106,9 @@ bool PathEdge::perturbDirection(const Scene *scene,
|
||||||
}
|
}
|
||||||
d = ray.d;
|
d = ray.d;
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!medium) {
|
if (!medium) {
|
||||||
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
||||||
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
||||||
|
|
|
@ -251,8 +251,8 @@ extern "C" {
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
Bitmap::Bitmap(EPixelFormat pFormat, EComponentFormat cFormat,
|
Bitmap::Bitmap(EPixelFormat pFormat, EComponentFormat cFormat,
|
||||||
const Vector2i &size, int channelCount) : m_pixelFormat(pFormat),
|
const Vector2i &size, uint8_t channelCount, uint8_t *data) : m_pixelFormat(pFormat),
|
||||||
m_componentFormat(cFormat), m_size(size), m_channelCount(channelCount) {
|
m_componentFormat(cFormat), m_size(size), m_data(data), m_channelCount(channelCount), m_ownsData(false) {
|
||||||
AssertEx(size.x > 0 && size.y > 0, "Invalid bitmap size");
|
AssertEx(size.x > 0 && size.y > 0, "Invalid bitmap size");
|
||||||
|
|
||||||
if (m_componentFormat == EUInt8)
|
if (m_componentFormat == EUInt8)
|
||||||
|
@ -262,10 +262,13 @@ Bitmap::Bitmap(EPixelFormat pFormat, EComponentFormat cFormat,
|
||||||
|
|
||||||
updateChannelCount();
|
updateChannelCount();
|
||||||
|
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
if (!m_data) {
|
||||||
|
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||||
|
m_ownsData = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap::Bitmap(EFileFormat format, Stream *stream, const std::string &prefix) : m_data(NULL) {
|
Bitmap::Bitmap(EFileFormat format, Stream *stream, const std::string &prefix) : m_data(NULL), m_ownsData(false) {
|
||||||
if (format == EAuto) {
|
if (format == EAuto) {
|
||||||
/* Try to automatically detect the file format */
|
/* Try to automatically detect the file format */
|
||||||
size_t pos = stream->getPos();
|
size_t pos = stream->getPos();
|
||||||
|
@ -336,7 +339,7 @@ void Bitmap::write(EFileFormat format, Stream *stream, int compression,
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Bitmap::getBufferSize() const {
|
size_t Bitmap::getBufferSize() const {
|
||||||
size_t bitsPerRow = m_size.x * m_channelCount * getBitsPerComponent();
|
size_t bitsPerRow = (size_t) m_size.x * m_channelCount * getBitsPerComponent();
|
||||||
size_t bytesPerRow = (bitsPerRow + 7) / 8; // round up to full bytes
|
size_t bytesPerRow = (bitsPerRow + 7) / 8; // round up to full bytes
|
||||||
return bytesPerRow * (size_t) m_size.y;
|
return bytesPerRow * (size_t) m_size.y;
|
||||||
}
|
}
|
||||||
|
@ -392,7 +395,7 @@ int Bitmap::getBytesPerComponent() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap::~Bitmap() {
|
Bitmap::~Bitmap() {
|
||||||
if (m_data)
|
if (m_data && m_ownsData)
|
||||||
freeAligned(m_data);
|
freeAligned(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +505,7 @@ void Bitmap::accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const size_t
|
const size_t
|
||||||
columns = size.x * m_channelCount,
|
columns = (size_t) size.x * m_channelCount,
|
||||||
pixelStride = getBytesPerPixel(),
|
pixelStride = getBytesPerPixel(),
|
||||||
sourceStride = bitmap->getWidth() * pixelStride,
|
sourceStride = bitmap->getWidth() * pixelStride,
|
||||||
targetStride = getWidth() * pixelStride;
|
targetStride = getWidth() * pixelStride;
|
||||||
|
@ -555,6 +558,250 @@ void Bitmap::accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bitmap::scale(Float value) {
|
||||||
|
if (m_componentFormat == EBitmask)
|
||||||
|
Log(EError, "Bitmap::scale(): bitmasks are not supported!");
|
||||||
|
|
||||||
|
size_t nPixels = getPixelCount(), nChannels = getChannelCount();
|
||||||
|
|
||||||
|
if (hasAlpha()) {
|
||||||
|
switch (m_componentFormat) {
|
||||||
|
case EUInt8: {
|
||||||
|
uint8_t *data = (uint8_t *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (uint8_t) std::min((Float) 0xFF,
|
||||||
|
std::max((Float) 0, *data * value + (Float) 0.5f));
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt16: {
|
||||||
|
uint16_t *data = (uint16_t *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (uint16_t) std::min((Float) 0xFFFF,
|
||||||
|
std::max((Float) 0, *data * value + (Float) 0.5f));
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt32: {
|
||||||
|
uint32_t *data = (uint32_t *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (uint32_t) std::min((Float) 0xFFFFFFFFUL,
|
||||||
|
std::max((Float) 0, *data * value + (Float) 0.5f));
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat16: {
|
||||||
|
half *data = (half *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (half) (*data * value); ++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat32: {
|
||||||
|
float *data = (float *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (float) (*data * value); ++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat64: {
|
||||||
|
double *data = (double *) m_data;
|
||||||
|
for (size_t i=0; i<nPixels; ++i) {
|
||||||
|
for (size_t j=0; j<nChannels-1; ++j) {
|
||||||
|
*data = (double) (*data * value); ++data;
|
||||||
|
}
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log(EError, "Bitmap::scale(): unexpected data format!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
size_t nEntries = nPixels * nChannels;
|
||||||
|
|
||||||
|
switch (m_componentFormat) {
|
||||||
|
case EUInt8: {
|
||||||
|
uint8_t *data = (uint8_t *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (uint8_t) std::min((Float) 0xFF,
|
||||||
|
std::max((Float) 0, data[i] * value + (Float) 0.5f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt16: {
|
||||||
|
uint16_t *data = (uint16_t *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (uint16_t) std::min((Float) 0xFFFF,
|
||||||
|
std::max((Float) 0, data[i] * value + (Float) 0.5f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt32: {
|
||||||
|
uint32_t *data = (uint32_t *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (uint32_t) std::min((Float) 0xFFFFFFFFUL,
|
||||||
|
std::max((Float) 0, data[i] * value + (Float) 0.5f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat16: {
|
||||||
|
half *data = (half *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (half) (data[i] * value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat32: {
|
||||||
|
float *data = (float *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (float) (data[i] * value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat64: {
|
||||||
|
double *data = (double *) m_data;
|
||||||
|
for (size_t i=0; i<nEntries; ++i)
|
||||||
|
data[i] = (double) (data[i] * value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log(EError, "Bitmap::scale(): unexpected data format!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> Bitmap::arithmeticOperation(Bitmap::EArithmeticOperation operation, const Bitmap *_bitmap1, const Bitmap *_bitmap2) {
|
||||||
|
ref<const Bitmap> bitmap1(_bitmap1), bitmap2(_bitmap2);
|
||||||
|
|
||||||
|
/* Determine the 'fancier' pixel / component format by a maximum operation on the enum values */
|
||||||
|
EPixelFormat pxFmt = (EPixelFormat) std::max(bitmap1->getPixelFormat(), bitmap2->getPixelFormat());
|
||||||
|
EComponentFormat cFmt = (EComponentFormat) std::max(bitmap1->getComponentFormat(), bitmap2->getComponentFormat());
|
||||||
|
|
||||||
|
if (cFmt == EBitmask)
|
||||||
|
Log(EError, "Bitmap::arithmeticOperation(): bitmasks are not supported!");
|
||||||
|
|
||||||
|
/* Make sure that the images match in size (resample if necessary) */
|
||||||
|
Vector2i size(
|
||||||
|
std::max(bitmap1->getWidth(), bitmap2->getWidth()),
|
||||||
|
std::max(bitmap1->getHeight(), bitmap2->getHeight()));
|
||||||
|
|
||||||
|
if (bitmap1->getSize() != size) {
|
||||||
|
bitmap1 = bitmap1->resample(NULL,
|
||||||
|
ReconstructionFilter::EClamp,
|
||||||
|
ReconstructionFilter::EClamp, size,
|
||||||
|
-std::numeric_limits<Float>::infinity(),
|
||||||
|
std::numeric_limits<Float>::infinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmap2->getSize() != size) {
|
||||||
|
bitmap2 = bitmap2->resample(NULL,
|
||||||
|
ReconstructionFilter::EClamp,
|
||||||
|
ReconstructionFilter::EClamp, size,
|
||||||
|
-std::numeric_limits<Float>::infinity(),
|
||||||
|
std::numeric_limits<Float>::infinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert the image format appropriately (no-op, if the format already matches) */
|
||||||
|
bitmap1 = const_cast<Bitmap *>(bitmap1.get())->convert(pxFmt, cFmt);
|
||||||
|
bitmap2 = const_cast<Bitmap *>(bitmap2.get())->convert(pxFmt, cFmt);
|
||||||
|
|
||||||
|
ref<Bitmap> output = new Bitmap(pxFmt, cFmt, size);
|
||||||
|
size_t nValues = output->getPixelCount() * output->getChannelCount();
|
||||||
|
|
||||||
|
#define IMPLEMENT_OPS() \
|
||||||
|
switch (operation) { \
|
||||||
|
case EAddition: for (size_t i=0; i<nValues; ++i) dst[i] = src1[i] + src2[i]; break; \
|
||||||
|
case ESubtraction: for (size_t i=0; i<nValues; ++i) dst[i] = src1[i] - src2[i]; break; \
|
||||||
|
case EMultiplication: for (size_t i=0; i<nValues; ++i) dst[i] = src1[i] * src2[i]; break; \
|
||||||
|
case EDivision: for (size_t i=0; i<nValues; ++i) dst[i] = src1[i] / src2[i]; break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cFmt) {
|
||||||
|
case EUInt8: {
|
||||||
|
const uint8_t *src1 = bitmap1->getUInt8Data();
|
||||||
|
const uint8_t *src2 = bitmap2->getUInt8Data();
|
||||||
|
uint8_t *dst = output->getUInt8Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt16: {
|
||||||
|
const uint16_t *src1 = bitmap1->getUInt16Data();
|
||||||
|
const uint16_t *src2 = bitmap2->getUInt16Data();
|
||||||
|
uint16_t *dst = output->getUInt16Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EUInt32: {
|
||||||
|
const uint32_t *src1 = bitmap1->getUInt32Data();
|
||||||
|
const uint32_t *src2 = bitmap2->getUInt32Data();
|
||||||
|
uint32_t *dst = output->getUInt32Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat16: {
|
||||||
|
const half *src1 = bitmap1->getFloat16Data();
|
||||||
|
const half *src2 = bitmap2->getFloat16Data();
|
||||||
|
half *dst = output->getFloat16Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat32: {
|
||||||
|
const float *src1 = bitmap1->getFloat32Data();
|
||||||
|
const float *src2 = bitmap2->getFloat32Data();
|
||||||
|
float *dst = output->getFloat32Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EFloat64: {
|
||||||
|
const double *src1 = bitmap1->getFloat64Data();
|
||||||
|
const double *src2 = bitmap2->getFloat64Data();
|
||||||
|
double *dst = output->getFloat64Data();
|
||||||
|
IMPLEMENT_OPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log(EError, "Bitmap::arithmeticOperation(): unexpected data format!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef IMPLEMENT_OPS
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Bitmap::colorBalance(Float r, Float g, Float b) {
|
void Bitmap::colorBalance(Float r, Float g, Float b) {
|
||||||
if (m_pixelFormat != ERGB && m_pixelFormat != ERGBA)
|
if (m_pixelFormat != ERGB && m_pixelFormat != ERGBA)
|
||||||
Log(EError, "colorBalance(): expected a RGB or RGBA image!");
|
Log(EError, "colorBalance(): expected a RGB or RGBA image!");
|
||||||
|
@ -1132,9 +1379,8 @@ void Bitmap::applyMatrix(Float matrix_[3][3]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Bitmap resampling utility function
|
/// Bitmap resampling utility function
|
||||||
template <typename Scalar> static void resample(const ReconstructionFilter *rfilter,
|
template <typename Scalar> static void resample(ref<const ReconstructionFilter> rfilter,
|
||||||
ReconstructionFilter::EBoundaryCondition bch,
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
ReconstructionFilter::EBoundaryCondition bcv,
|
ReconstructionFilter::EBoundaryCondition bcv,
|
||||||
const Bitmap *source, Bitmap *target, Float minValue, Float maxValue) {
|
const Bitmap *source, Bitmap *target, Float minValue, Float maxValue) {
|
||||||
|
@ -1142,6 +1388,17 @@ template <typename Scalar> static void resample(const ReconstructionFilter *rfil
|
||||||
|
|
||||||
int channels = source->getChannelCount();
|
int channels = source->getChannelCount();
|
||||||
|
|
||||||
|
if (!rfilter) {
|
||||||
|
/* Resample using a 2-lobed Lanczos reconstruction filter */
|
||||||
|
Properties rfilterProps("lanczos");
|
||||||
|
rfilterProps.setInteger("lobes", 2);
|
||||||
|
ReconstructionFilter *instance = static_cast<ReconstructionFilter *> (
|
||||||
|
PluginManager::getInstance()->createObject(
|
||||||
|
MTS_CLASS(ReconstructionFilter), rfilterProps));
|
||||||
|
instance->configure();
|
||||||
|
rfilter = instance;
|
||||||
|
}
|
||||||
|
|
||||||
if (source->getWidth() != target->getWidth()) {
|
if (source->getWidth() != target->getWidth()) {
|
||||||
/* Re-sample along the X direction */
|
/* Re-sample along the X direction */
|
||||||
Resampler<Scalar> r(rfilter, bch, source->getWidth(), target->getWidth());
|
Resampler<Scalar> r(rfilter, bch, source->getWidth(), target->getWidth());
|
||||||
|
@ -1349,6 +1606,7 @@ void Bitmap::readPNG(Stream *stream) {
|
||||||
|
|
||||||
size_t bufferSize = getBufferSize();
|
size_t bufferSize = getBufferSize();
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
||||||
|
m_ownsData = true;
|
||||||
rows = new png_bytep[m_size.y];
|
rows = new png_bytep[m_size.y];
|
||||||
size_t rowBytes = png_get_rowbytes(png_ptr, info_ptr);
|
size_t rowBytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
Assert(rowBytes == getBufferSize() / m_size.y);
|
Assert(rowBytes == getBufferSize() / m_size.y);
|
||||||
|
@ -1505,6 +1763,7 @@ void Bitmap::readJPEG(Stream *stream) {
|
||||||
* (size_t) cinfo.output_components;
|
* (size_t) cinfo.output_components;
|
||||||
|
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||||
|
m_ownsData = true;
|
||||||
|
|
||||||
boost::scoped_array<uint8_t*> scanlines(new uint8_t*[m_size.y]);
|
boost::scoped_array<uint8_t*> scanlines(new uint8_t*[m_size.y]);
|
||||||
for (int i=0; i<m_size.y; ++i)
|
for (int i=0; i<m_size.y; ++i)
|
||||||
|
@ -1756,7 +2015,7 @@ void Bitmap::readOpenEXR(Stream *stream, const std::string &_prefix) {
|
||||||
|
|
||||||
updateChannelCount();
|
updateChannelCount();
|
||||||
m_gamma = 1.0f;
|
m_gamma = 1.0f;
|
||||||
Assert(m_channelCount == (int) sourceChannels.size());
|
Assert(m_channelCount == (uint8_t) sourceChannels.size());
|
||||||
|
|
||||||
Imf::PixelType pxType = channels[sourceChannels[0]].type;
|
Imf::PixelType pxType = channels[sourceChannels[0]].type;
|
||||||
|
|
||||||
|
@ -1792,11 +2051,12 @@ void Bitmap::readOpenEXR(Stream *stream, const std::string &_prefix) {
|
||||||
|
|
||||||
/* Finally, allocate memory for it */
|
/* Finally, allocate memory for it */
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||||
|
m_ownsData = true;
|
||||||
char *ptr = (char *) m_data;
|
char *ptr = (char *) m_data;
|
||||||
|
|
||||||
ptr -= (dataWindow.min.x + dataWindow.min.y * m_size.x) * pixelStride;
|
ptr -= (dataWindow.min.x + dataWindow.min.y * m_size.x) * pixelStride;
|
||||||
|
|
||||||
ref_vector<Bitmap> resampleBuffers(m_channelCount);
|
ref_vector<Bitmap> resampleBuffers((size_t) m_channelCount);
|
||||||
ref<ReconstructionFilter> rfilter;
|
ref<ReconstructionFilter> rfilter;
|
||||||
|
|
||||||
/* Tell OpenEXR where the image data should be put */
|
/* Tell OpenEXR where the image data should be put */
|
||||||
|
@ -2169,6 +2429,7 @@ void Bitmap::readTGA(Stream *stream) {
|
||||||
rowSize = bufferSize / height;
|
rowSize = bufferSize / height;
|
||||||
|
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
||||||
|
m_ownsData = true;
|
||||||
int channels = bpp/8;
|
int channels = bpp/8;
|
||||||
|
|
||||||
if (!rle) {
|
if (!rle) {
|
||||||
|
@ -2259,6 +2520,7 @@ void Bitmap::readBMP(Stream *stream) {
|
||||||
|
|
||||||
size_t bufferSize = getBufferSize();
|
size_t bufferSize = getBufferSize();
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
||||||
|
m_ownsData = true;
|
||||||
|
|
||||||
Log(ETrace, "Loading a %ix%i BMP file", m_size.x, m_size.y);
|
Log(ETrace, "Loading a %ix%i BMP file", m_size.x, m_size.y);
|
||||||
|
|
||||||
|
@ -2396,6 +2658,7 @@ void Bitmap::readRGBE(Stream *stream) {
|
||||||
m_channelCount = 3;
|
m_channelCount = 3;
|
||||||
m_gamma = 1.0f;
|
m_gamma = 1.0f;
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||||
|
m_ownsData = true;
|
||||||
float *data = (float *) m_data;
|
float *data = (float *) m_data;
|
||||||
|
|
||||||
if (m_size.x < 8 || m_size.x > 0x7fff) {
|
if (m_size.x < 8 || m_size.x > 0x7fff) {
|
||||||
|
@ -2573,6 +2836,7 @@ void Bitmap::readPFM(Stream *stream) {
|
||||||
SLog(EError, "Could not parse scale/order information!");
|
SLog(EError, "Could not parse scale/order information!");
|
||||||
|
|
||||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||||
|
m_ownsData = true;
|
||||||
float *data = (float *) m_data;
|
float *data = (float *) m_data;
|
||||||
|
|
||||||
Stream::EByteOrder backup = stream->getByteOrder();
|
Stream::EByteOrder backup = stream->getByteOrder();
|
||||||
|
@ -2626,7 +2890,7 @@ void Bitmap::writePFM(Stream *stream) const {
|
||||||
float *dest = temp;
|
float *dest = temp;
|
||||||
|
|
||||||
for (int x=0; x<m_size.x; ++x) {
|
for (int x=0; x<m_size.x; ++x) {
|
||||||
for (int j=0; j<m_channelCount-1; ++j)
|
for (uint8_t j=0; j<m_channelCount-1; ++j)
|
||||||
*dest++ = *source++;
|
*dest++ = *source++;
|
||||||
source++;
|
source++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -631,8 +631,9 @@ void Worker::start(Scheduler *scheduler, int workerIndex, int coreOffset) {
|
||||||
Thread::start();
|
Thread::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalWorker::LocalWorker(const std::string &name,
|
LocalWorker::LocalWorker(int coreID, const std::string &name,
|
||||||
Thread::EThreadPriority priority) : Worker(name) {
|
Thread::EThreadPriority priority) : Worker(name) {
|
||||||
|
setCoreAffinity(coreID);
|
||||||
m_coreCount = 1;
|
m_coreCount = 1;
|
||||||
#if !defined(__LINUX__)
|
#if !defined(__LINUX__)
|
||||||
/* Don't set thead priority on Linux, since it uses
|
/* Don't set thead priority on Linux, since it uses
|
||||||
|
|
|
@ -130,7 +130,7 @@ Float integrateCubicInterp1DN(size_t idx, const Float *nodes, const Float *value
|
||||||
return ((d0-d1) * (Float) (1.0 / 12.0) + (f0+f1) * 0.5f) * width;
|
return ((d0-d1) * (Float) (1.0 / 12.0) + (f0+f1) * 0.5f) * width;
|
||||||
}
|
}
|
||||||
|
|
||||||
Float sampleCubicInterp1D(size_t idx, Float *values, size_t size, Float min,
|
Float sampleCubicInterp1D(size_t idx, const Float *values, size_t size, Float min,
|
||||||
Float max, Float sample, Float *fval) {
|
Float max, Float sample, Float *fval) {
|
||||||
Float f0 = values[idx], f1 = values[idx+1], d0, d1;
|
Float f0 = values[idx], f1 = values[idx+1], d0, d1;
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ Float sampleCubicInterp1D(size_t idx, Float *values, size_t size, Float min,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Float sampleCubicInterp1DN(size_t idx, Float *nodes, Float *values,
|
Float sampleCubicInterp1DN(size_t idx, const Float *nodes, const Float *values,
|
||||||
size_t size, Float sample, Float *fval) {
|
size_t size, Float sample, Float *fval) {
|
||||||
Float f0 = values[idx],
|
Float f0 = values[idx],
|
||||||
f1 = values[idx+1],
|
f1 = values[idx+1],
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <mitsuba/core/lock.h>
|
#include <mitsuba/core/lock.h>
|
||||||
#include <mitsuba/core/fresolver.h>
|
#include <mitsuba/core/fresolver.h>
|
||||||
|
#include <mitsuba/core/atomic.h>
|
||||||
#if defined(MTS_OPENMP)
|
#if defined(MTS_OPENMP)
|
||||||
# include <omp.h>
|
# include <omp.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -35,7 +36,6 @@
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
namespace {
|
namespace {
|
||||||
// Helper function to set a native thread name. MSDN:
|
// Helper function to set a native thread name. MSDN:
|
||||||
|
@ -69,6 +69,13 @@ void SetThreadName(const char* threadName, DWORD dwThreadID = -1) {
|
||||||
} // namespace
|
} // namespace
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#if defined(__LINUX__) || defined(__OSX__)
|
||||||
|
static pthread_key_t __thread_id;
|
||||||
|
#elif defined(__WINDOWS__)
|
||||||
|
__declspec(thread) int __thread_id;
|
||||||
|
#endif
|
||||||
|
static int __thread_id_ctr = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal Thread members
|
* Internal Thread members
|
||||||
*/
|
*/
|
||||||
|
@ -80,13 +87,15 @@ struct Thread::ThreadPrivate {
|
||||||
std::string name;
|
std::string name;
|
||||||
bool running, joined;
|
bool running, joined;
|
||||||
Thread::EThreadPriority priority;
|
Thread::EThreadPriority priority;
|
||||||
|
int coreAffinity;
|
||||||
static ThreadLocal<Thread> *self;
|
static ThreadLocal<Thread> *self;
|
||||||
bool critical;
|
bool critical;
|
||||||
boost::thread thread;
|
boost::thread thread;
|
||||||
|
|
||||||
ThreadPrivate(const std::string & name_) :
|
ThreadPrivate(const std::string & name_) :
|
||||||
name(name_), running(false), joined(false),
|
name(name_), running(false), joined(false),
|
||||||
priority(Thread::ENormalPriority), critical(false) { }
|
priority(Thread::ENormalPriority), coreAffinity(-1),
|
||||||
|
critical(false) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,11 +148,12 @@ bool Thread::getCritical() const {
|
||||||
|
|
||||||
int Thread::getID() {
|
int Thread::getID() {
|
||||||
#if defined(__WINDOWS__)
|
#if defined(__WINDOWS__)
|
||||||
return static_cast<int>(GetCurrentThreadId());
|
return __thread_id;
|
||||||
#elif defined(__OSX__)
|
#elif defined(__OSX__) || defined(__LINUX__)
|
||||||
return static_cast<int>(pthread_mach_thread_np(pthread_self()));
|
/* pthread_self() doesn't provide nice increasing IDs, and syscall(SYS_gettid)
|
||||||
#else
|
causes a context switch. Thus, this function uses a thread-local variable
|
||||||
return (int) pthread_self();
|
to provide a nice linearly increasing sequence of thread IDs */
|
||||||
|
return static_cast<int>(reinterpret_cast<intptr_t>(pthread_getspecific(__thread_id)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,9 +307,71 @@ bool Thread::setPriority(EThreadPriority priority) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::setCoreAffinity(int coreID) {
|
||||||
|
d->coreAffinity = coreID;
|
||||||
|
if (!d->running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if defined(__OSX__)
|
||||||
|
/* CPU affinity not supported on OSX */
|
||||||
|
#elif defined(__LINUX__)
|
||||||
|
int nCores = getCoreCount();
|
||||||
|
cpu_set_t *cpuset = CPU_ALLOC(nCores);
|
||||||
|
if (cpuset == NULL)
|
||||||
|
Log(EError, "Thread::setCoreAffinity(): could not allocate cpu_set_t");
|
||||||
|
|
||||||
|
size_t size = CPU_ALLOC_SIZE(nCores);
|
||||||
|
CPU_ZERO_S(size, cpuset);
|
||||||
|
if (coreID != -1 && coreID < nCores) {
|
||||||
|
CPU_SET_S(coreID, size, cpuset);
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<nCores; ++i)
|
||||||
|
CPU_SET_S(i, size, cpuset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pthread_t threadID = d->thread.native_handle();
|
||||||
|
if (pthread_setaffinity_np(threadID, size, cpuset) != 0)
|
||||||
|
Log(EWarn, "Thread::setCoreAffinity(): pthread_setaffinity_np: failed");
|
||||||
|
CPU_FREE(cpuset);
|
||||||
|
#elif defined(__WINDOWS__)
|
||||||
|
int nCores = getCoreCount();
|
||||||
|
const HANDLE handle = d->thread.native_handle();
|
||||||
|
|
||||||
|
DWORD_PTR mask;
|
||||||
|
|
||||||
|
if (coreID != -1 && coreID < nCores)
|
||||||
|
mask = (DWORD_PTR) 1 << coreID;
|
||||||
|
else
|
||||||
|
mask = (1 << nCores) - 1;
|
||||||
|
|
||||||
|
if (!SetThreadAffinityMask(handle, mask))
|
||||||
|
Log(EWarn, "Thread::setCoreAffinity(): SetThreadAffinityMask : failed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int Thread::getCoreAffinity() const {
|
||||||
|
return d->coreAffinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Thread::dispatch(Thread *thread) {
|
void Thread::dispatch(Thread *thread) {
|
||||||
detail::initializeLocalTLS();
|
detail::initializeLocalTLS();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#if defined(__LINUX__)
|
||||||
|
pthread_setspecific(__thread_id, reinterpret_cast<void *>(syscall(SYS_gettid)));
|
||||||
|
#elif defined(__OSX__)
|
||||||
|
pthread_setspecific(__thread_id, reinterpret_cast<void *>(pthread_mach_thread_np(pthread_self())));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int id = atomicAdd(&__thread_id_ctr, 1);
|
||||||
|
#if defined(__LINUX__) || defined(__OSX__)
|
||||||
|
pthread_setspecific(__thread_id, reinterpret_cast<void *>(id));
|
||||||
|
#elif defined(__WINDOWS__)
|
||||||
|
__thread_id = id;
|
||||||
|
#endif
|
||||||
|
|
||||||
Thread::ThreadPrivate::self->set(thread);
|
Thread::ThreadPrivate::self->set(thread);
|
||||||
|
|
||||||
if (thread->getPriority() != ENormalPriority)
|
if (thread->getPriority() != ENormalPriority)
|
||||||
|
@ -319,6 +391,9 @@ void Thread::dispatch(Thread *thread) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thread->getCoreAffinity() != -1)
|
||||||
|
thread->setCoreAffinity(thread->getCoreAffinity());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
thread->run();
|
thread->run();
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
|
@ -410,12 +485,15 @@ int mts_omp_get_thread_num() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Thread::staticInitialization() {
|
void Thread::staticInitialization() {
|
||||||
#if defined(__OSX__)
|
#if defined(__OSX__)
|
||||||
__mts_autorelease_init();
|
__mts_autorelease_init();
|
||||||
#if defined(MTS_OPENMP)
|
#if defined(MTS_OPENMP)
|
||||||
__omp_threadCount = omp_get_max_threads();
|
__omp_threadCount = omp_get_max_threads();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__LINUX__) || defined(__OSX__)
|
||||||
|
pthread_key_create(&__thread_id, NULL);
|
||||||
|
#endif
|
||||||
detail::initializeGlobalTLS();
|
detail::initializeGlobalTLS();
|
||||||
detail::initializeLocalTLS();
|
detail::initializeLocalTLS();
|
||||||
|
|
||||||
|
@ -451,6 +529,9 @@ void Thread::staticShutdown() {
|
||||||
delete ThreadPrivate::self;
|
delete ThreadPrivate::self;
|
||||||
ThreadPrivate::self = NULL;
|
ThreadPrivate::self = NULL;
|
||||||
detail::destroyGlobalTLS();
|
detail::destroyGlobalTLS();
|
||||||
|
#if defined(__LINUX__) || defined(__OSX__)
|
||||||
|
pthread_key_delete(__thread_id);
|
||||||
|
#endif
|
||||||
#if defined(__OSX__)
|
#if defined(__OSX__)
|
||||||
#if defined(MTS_OPENMP)
|
#if defined(MTS_OPENMP)
|
||||||
if (__omp_key_created)
|
if (__omp_key_created)
|
||||||
|
@ -505,6 +586,13 @@ void Thread::initializeOpenMP(size_t threadCount) {
|
||||||
SetThreadName(threadName.c_str());
|
SetThreadName(threadName.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int id = atomicAdd(&__thread_id_ctr, 1);
|
||||||
|
#if defined(__LINUX__) || defined(__OSX__)
|
||||||
|
pthread_setspecific(__thread_id, reinterpret_cast<void *>(id));
|
||||||
|
#elif defined(__WINDOWS__)
|
||||||
|
__thread_id = id;
|
||||||
|
#endif
|
||||||
|
|
||||||
thread->d->running = false;
|
thread->d->running = false;
|
||||||
thread->d->joined = false;
|
thread->d->joined = false;
|
||||||
thread->d->fresolver = fResolver;
|
thread->d->fresolver = fResolver;
|
||||||
|
|
|
@ -829,12 +829,11 @@ std::string memString(size_t size, bool precise) {
|
||||||
|
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << std::setprecision(suffix == 0 ? 0 : (precise ? 4 : 1))
|
os << std::setprecision(suffix == 0 ? 0 : (precise ? 4 : 1))
|
||||||
<< std::fixed << value << suffixes[suffix];
|
<< std::fixed << value << " " << suffixes[suffix];
|
||||||
|
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Float hypot2(Float a, Float b) {
|
Float hypot2(Float a, Float b) {
|
||||||
Float r;
|
Float r;
|
||||||
if (std::abs(a) > std::abs(b)) {
|
if (std::abs(a) > std::abs(b)) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|
||||||
ConstantSpectrumTexture::ConstantSpectrumTexture(Stream *stream, InstanceManager *manager)
|
ConstantSpectrumTexture::ConstantSpectrumTexture(Stream *stream, InstanceManager *manager)
|
||||||
: Texture(stream, manager) {
|
: Texture(stream, manager) {
|
||||||
m_value = Spectrum(stream);
|
m_value = Spectrum(stream);
|
||||||
|
@ -31,6 +32,12 @@ void ConstantSpectrumTexture::serialize(Stream *stream, InstanceManager *manager
|
||||||
m_value.serialize(stream);
|
m_value.serialize(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> ConstantSpectrumTexture::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> result = new Bitmap(Bitmap::ESpectrum, Bitmap::EFloat, Vector2i(1, 1));
|
||||||
|
*((Spectrum *) result->getFloatData()) = m_value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
ConstantFloatTexture::ConstantFloatTexture(Stream *stream, InstanceManager *manager)
|
ConstantFloatTexture::ConstantFloatTexture(Stream *stream, InstanceManager *manager)
|
||||||
: Texture(stream, manager) {
|
: Texture(stream, manager) {
|
||||||
m_value = stream->readFloat();
|
m_value = stream->readFloat();
|
||||||
|
@ -41,6 +48,12 @@ void ConstantFloatTexture::serialize(Stream *stream, InstanceManager *manager) c
|
||||||
stream->writeFloat(m_value);
|
stream->writeFloat(m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> ConstantFloatTexture::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> result = new Bitmap(Bitmap::ELuminance, Bitmap::EFloat, Vector2i(1, 1));
|
||||||
|
*result->getFloatData() = m_value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
SpectrumProductTexture::SpectrumProductTexture(Stream *stream, InstanceManager *manager)
|
SpectrumProductTexture::SpectrumProductTexture(Stream *stream, InstanceManager *manager)
|
||||||
: Texture(stream, manager) {
|
: Texture(stream, manager) {
|
||||||
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
|
@ -53,6 +66,12 @@ void SpectrumProductTexture::serialize(Stream *stream, InstanceManager *manager)
|
||||||
manager->serialize(stream, m_b.get());
|
manager->serialize(stream, m_b.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> SpectrumProductTexture::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> bitmap1 = m_a->getBitmap(sizeHint);
|
||||||
|
ref<Bitmap> bitmap2 = m_b->getBitmap(sizeHint);
|
||||||
|
return Bitmap::arithmeticOperation(Bitmap::EMultiplication, bitmap1.get(), bitmap2.get());
|
||||||
|
}
|
||||||
|
|
||||||
SpectrumAdditionTexture::SpectrumAdditionTexture(Stream *stream, InstanceManager *manager)
|
SpectrumAdditionTexture::SpectrumAdditionTexture(Stream *stream, InstanceManager *manager)
|
||||||
: Texture(stream, manager) {
|
: Texture(stream, manager) {
|
||||||
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
|
@ -65,6 +84,12 @@ void SpectrumAdditionTexture::serialize(Stream *stream, InstanceManager *manager
|
||||||
manager->serialize(stream, m_b.get());
|
manager->serialize(stream, m_b.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> SpectrumAdditionTexture::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> bitmap1 = m_a->getBitmap(sizeHint);
|
||||||
|
ref<Bitmap> bitmap2 = m_b->getBitmap(sizeHint);
|
||||||
|
return Bitmap::arithmeticOperation(Bitmap::EAddition, bitmap1.get(), bitmap2.get());
|
||||||
|
}
|
||||||
|
|
||||||
SpectrumSubtractionTexture::SpectrumSubtractionTexture(Stream *stream, InstanceManager *manager)
|
SpectrumSubtractionTexture::SpectrumSubtractionTexture(Stream *stream, InstanceManager *manager)
|
||||||
: Texture(stream, manager) {
|
: Texture(stream, manager) {
|
||||||
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
m_a = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
|
@ -77,6 +102,12 @@ void SpectrumSubtractionTexture::serialize(Stream *stream, InstanceManager *mana
|
||||||
manager->serialize(stream, m_b.get());
|
manager->serialize(stream, m_b.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> SpectrumSubtractionTexture::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> bitmap1 = m_a->getBitmap(sizeHint);
|
||||||
|
ref<Bitmap> bitmap2 = m_b->getBitmap(sizeHint);
|
||||||
|
return Bitmap::arithmeticOperation(Bitmap::ESubtraction, bitmap1.get(), bitmap2.get());
|
||||||
|
}
|
||||||
|
|
||||||
class ConstantSpectrumTextureShader : public Shader {
|
class ConstantSpectrumTextureShader : public Shader {
|
||||||
public:
|
public:
|
||||||
ConstantSpectrumTextureShader(Renderer *renderer, const Spectrum &value)
|
ConstantSpectrumTextureShader(Renderer *renderer, const Spectrum &value)
|
||||||
|
|
|
@ -638,6 +638,8 @@ void export_core() {
|
||||||
.def("getID", &Thread::getID)
|
.def("getID", &Thread::getID)
|
||||||
.def("setPriority", &Thread::setPriority)
|
.def("setPriority", &Thread::setPriority)
|
||||||
.def("getPriority", &Thread::getPriority)
|
.def("getPriority", &Thread::getPriority)
|
||||||
|
.def("setCoreAffinity", &Thread::setCoreAffinity)
|
||||||
|
.def("getCoreAffinity", &Thread::getCoreAffinity)
|
||||||
.def("setCritical", &Thread::setCritical)
|
.def("setCritical", &Thread::setCritical)
|
||||||
.def("getCritical", &Thread::getCritical)
|
.def("getCritical", &Thread::getCritical)
|
||||||
.def("setName", &Thread::setName)
|
.def("setName", &Thread::setName)
|
||||||
|
@ -871,8 +873,8 @@ void export_core() {
|
||||||
.def("getCoreCount", &Worker::getCoreCount)
|
.def("getCoreCount", &Worker::getCoreCount)
|
||||||
.def("isRemoteWorker", &Worker::isRemoteWorker);
|
.def("isRemoteWorker", &Worker::isRemoteWorker);
|
||||||
|
|
||||||
BP_CLASS(LocalWorker, Worker, bp::init<const std::string>())
|
BP_CLASS(LocalWorker, Worker, (bp::init<int, const std::string>()))
|
||||||
.def(bp::init<const std::string, Thread::EThreadPriority>());
|
.def(bp::init<int, const std::string, Thread::EThreadPriority>());
|
||||||
|
|
||||||
BP_CLASS(RemoteWorker, Worker, (bp::init<const std::string, Stream *>()))
|
BP_CLASS(RemoteWorker, Worker, (bp::init<const std::string, Stream *>()))
|
||||||
.def("getNodeName", &RemoteWorker::getNodeName, BP_RETURN_VALUE);
|
.def("getNodeName", &RemoteWorker::getNodeName, BP_RETURN_VALUE);
|
||||||
|
|
|
@ -351,12 +351,70 @@ void export_render() {
|
||||||
.def("serialize", triMesh_serialize2)
|
.def("serialize", triMesh_serialize2)
|
||||||
.def("writeOBJ", &TriMesh::writeOBJ);
|
.def("writeOBJ", &TriMesh::writeOBJ);
|
||||||
|
|
||||||
BP_CLASS(Sensor, ConfigurableObject, bp::no_init) // incomplete
|
Shape *(AbstractEmitter::*abstractemitter_getShape)(void) = &AbstractEmitter::getShape;
|
||||||
|
Medium *(AbstractEmitter::*abstractemitter_getMedium)(void) = &AbstractEmitter::getMedium;
|
||||||
|
|
||||||
|
BP_CLASS(AbstractEmitter, ConfigurableObject, bp::no_init)
|
||||||
|
.def("getType", &AbstractEmitter::getType)
|
||||||
|
.def("setWorldTransform", &AbstractEmitter::setWorldTransform)
|
||||||
|
.def("getWorldTransform", &AbstractEmitter::getWorldTransform, BP_RETURN_VALUE)
|
||||||
|
.def("isOnSurface", &AbstractEmitter::isOnSurface)
|
||||||
|
.def("needsPositionSample", &AbstractEmitter::needsPositionSample)
|
||||||
|
.def("needsDirectionSample", &AbstractEmitter::needsDirectionSample)
|
||||||
|
.def("needsDirectSample", &AbstractEmitter::needsDirectSample)
|
||||||
|
.def("getDirectMeasure", &AbstractEmitter::getDirectMeasure)
|
||||||
|
.def("isDegenerate", &AbstractEmitter::isDegenerate)
|
||||||
|
.def("getShape", abstractemitter_getShape, BP_RETURN_VALUE)
|
||||||
|
.def("getMedium", abstractemitter_getMedium, BP_RETURN_VALUE)
|
||||||
|
.def("createShape", &AbstractEmitter::createShape, BP_RETURN_VALUE)
|
||||||
|
.def("getAABB", &AbstractEmitter::getAABB, BP_RETURN_VALUE);
|
||||||
|
|
||||||
|
BP_SETSCOPE(AbstractEmitter_class);
|
||||||
|
bp::enum_<AbstractEmitter::EEmitterType>("EEmitterType")
|
||||||
|
.value("EDeltaDirection,", AbstractEmitter::EDeltaDirection)
|
||||||
|
.value("EDeltaPosition,", AbstractEmitter::EDeltaPosition)
|
||||||
|
.value("EOnSurface,", AbstractEmitter::EOnSurface)
|
||||||
|
.export_values();
|
||||||
|
BP_SETSCOPE(renderModule);
|
||||||
|
|
||||||
|
BP_CLASS(Emitter, AbstractEmitter, bp::no_init) // incomplete
|
||||||
|
.def("eval", &Emitter::eval, BP_RETURN_VALUE)
|
||||||
|
.def("getSamplingWeight", &Emitter::getSamplingWeight)
|
||||||
|
.def("isEnvironmentEmitter", &Emitter::isEnvironmentEmitter)
|
||||||
|
.def("evalEnvironment", &Emitter::evalEnvironment, BP_RETURN_VALUE);
|
||||||
|
|
||||||
|
BP_SETSCOPE(Emitter_class);
|
||||||
|
bp::enum_<Emitter::EEmitterFlags>("EEmitterFlags")
|
||||||
|
.value("EEnvironmentEmitter,", Emitter::EEnvironmentEmitter)
|
||||||
|
.export_values();
|
||||||
|
BP_SETSCOPE(renderModule);
|
||||||
|
|
||||||
|
BP_CLASS(Sensor, AbstractEmitter, bp::no_init) // incomplete
|
||||||
.def("getShutterOpen", &Sensor::getShutterOpen)
|
.def("getShutterOpen", &Sensor::getShutterOpen)
|
||||||
.def("setShutterOpen", &Sensor::setShutterOpen)
|
.def("setShutterOpen", &Sensor::setShutterOpen)
|
||||||
.def("getShutterOpenTime", &Sensor::getShutterOpenTime)
|
.def("getShutterOpenTime", &Sensor::getShutterOpenTime)
|
||||||
.def("setShutterOpenTime", &Sensor::setShutterOpenTime);
|
.def("setShutterOpenTime", &Sensor::setShutterOpenTime);
|
||||||
|
|
||||||
|
void (Film::*film_develop1)(const Scene *scene, Float renderTime) = &Film::develop;
|
||||||
|
bool (Film::*film_develop2)(const Point2i &offset, const Vector2i &size,
|
||||||
|
const Point2i &targetOffset, Bitmap *target) const = &Film::develop;
|
||||||
|
ReconstructionFilter *(Film::*film_getreconstructionfilter)() = &Film::getReconstructionFilter;
|
||||||
|
|
||||||
|
BP_CLASS(Film, ConfigurableObject, bp::no_init)
|
||||||
|
.def("getSize", &Film::getSize, BP_RETURN_VALUE)
|
||||||
|
.def("getCropSize", &Film::getCropSize, BP_RETURN_VALUE)
|
||||||
|
.def("getCropOffset", &Film::getCropOffset, BP_RETURN_VALUE)
|
||||||
|
.def("clear", &Film::clear)
|
||||||
|
.def("setBitmap", &Film::setBitmap)
|
||||||
|
.def("addBitmap", &Film::addBitmap)
|
||||||
|
.def("setDestinationFile", &Film::setDestinationFile)
|
||||||
|
.def("develop", film_develop1)
|
||||||
|
.def("develop", film_develop2)
|
||||||
|
.def("destinationExists", &Film::destinationExists)
|
||||||
|
.def("hasHighQualityEdges", &Film::hasHighQualityEdges)
|
||||||
|
.def("hasAlpha", &Film::hasAlpha)
|
||||||
|
.def("getReconstructionFilter", film_getreconstructionfilter, BP_RETURN_VALUE);
|
||||||
|
|
||||||
void (ProjectiveCamera::*projectiveCamera_setWorldTransform1)(const Transform &) = &ProjectiveCamera::setWorldTransform;
|
void (ProjectiveCamera::*projectiveCamera_setWorldTransform1)(const Transform &) = &ProjectiveCamera::setWorldTransform;
|
||||||
void (ProjectiveCamera::*projectiveCamera_setWorldTransform2)(AnimatedTransform *) = &ProjectiveCamera::setWorldTransform;
|
void (ProjectiveCamera::*projectiveCamera_setWorldTransform2)(AnimatedTransform *) = &ProjectiveCamera::setWorldTransform;
|
||||||
const Transform (ProjectiveCamera::*projectiveCamera_getWorldTransform1)(Float t) const = &ProjectiveCamera::getWorldTransform;
|
const Transform (ProjectiveCamera::*projectiveCamera_getWorldTransform1)(Float t) const = &ProjectiveCamera::getWorldTransform;
|
||||||
|
|
|
@ -333,7 +333,7 @@ void Scene::initialize() {
|
||||||
}
|
}
|
||||||
if (primitiveCount != effPrimitiveCount) {
|
if (primitiveCount != effPrimitiveCount) {
|
||||||
Log(EDebug, "Scene contains " SIZE_T_FMT " primitives. Due to "
|
Log(EDebug, "Scene contains " SIZE_T_FMT " primitives. Due to "
|
||||||
"instancing, the effective number of primitives is "
|
"instancing or other kinds of procedural geometry, the effective number of primitives is "
|
||||||
SIZE_T_FMT ".", primitiveCount, effPrimitiveCount);
|
SIZE_T_FMT ".", primitiveCount, effPrimitiveCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,11 +102,10 @@ SceneHandler::SceneHandler(const SAXParser *parser,
|
||||||
m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL);
|
m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL);
|
||||||
m_tags["spectrum"] = TagEntry(ESpectrum, (Class *) NULL);
|
m_tags["spectrum"] = TagEntry(ESpectrum, (Class *) NULL);
|
||||||
m_tags["transform"] = TagEntry(ETransform, (Class *) NULL);
|
m_tags["transform"] = TagEntry(ETransform, (Class *) NULL);
|
||||||
m_tags["animation"] = TagEntry(EAnimation, (Class *) NULL);
|
m_tags["animation"] = TagEntry(EAnimation, (Class *) NULL);
|
||||||
m_tags["include"] = TagEntry(EInclude, (Class *) NULL);
|
m_tags["include"] = TagEntry(EInclude, (Class *) NULL);
|
||||||
m_tags["alias"] = TagEntry(EAlias, (Class *) NULL);
|
m_tags["alias"] = TagEntry(EAlias, (Class *) NULL);
|
||||||
|
|
||||||
|
|
||||||
XMLTransService::Codes failReason;
|
XMLTransService::Codes failReason;
|
||||||
m_transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(
|
m_transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(
|
||||||
"UTF-8", failReason, TRANSCODE_BLOCKSIZE);
|
"UTF-8", failReason, TRANSCODE_BLOCKSIZE);
|
||||||
|
|
|
@ -47,7 +47,7 @@ Spectrum Texture::getMinimum() const { NotImplementedError("getMinimum"); }
|
||||||
Spectrum Texture::getMaximum() const { NotImplementedError("getMaximum"); }
|
Spectrum Texture::getMaximum() const { NotImplementedError("getMaximum"); }
|
||||||
bool Texture::isConstant() const { NotImplementedError("isConstant"); }
|
bool Texture::isConstant() const { NotImplementedError("isConstant"); }
|
||||||
bool Texture::usesRayDifferentials() const { NotImplementedError("usesRayDifferentials"); }
|
bool Texture::usesRayDifferentials() const { NotImplementedError("usesRayDifferentials"); }
|
||||||
ref<Bitmap> Texture::getBitmap() const { return NULL; }
|
ref<Bitmap> Texture::getBitmap(const Vector2i &) const { NotImplementedError("getBitmap"); }
|
||||||
|
|
||||||
ref<Texture> Texture::expand() {
|
ref<Texture> Texture::expand() {
|
||||||
return this;
|
return this;
|
||||||
|
@ -100,6 +100,22 @@ Spectrum Texture2D::eval(const Intersection &its, bool filter) const {
|
||||||
return eval(uv);
|
return eval(uv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> Texture2D::getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
Vector2i res(sizeHint);
|
||||||
|
if (res.x <= 0 || res.y <= 0)
|
||||||
|
res = Vector2i(32);
|
||||||
|
|
||||||
|
Float invX = 1.0f / res.x, invY = 1.0f / res.y;
|
||||||
|
|
||||||
|
ref<Bitmap> bitmap = new Bitmap(Bitmap::ESpectrum, Bitmap::EFloat, res);
|
||||||
|
Spectrum *target = (Spectrum *) bitmap->getFloatData();
|
||||||
|
for (int y=0; y<res.y; ++y)
|
||||||
|
for (int x=0; x<res.x; ++x)
|
||||||
|
*target++ = eval(Point2((x + 0.5f) * invX, (y + 0.5f) * invY));
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
MTS_IMPLEMENT_CLASS(Texture, true, ConfigurableObject)
|
MTS_IMPLEMENT_CLASS(Texture, true, ConfigurableObject)
|
||||||
MTS_IMPLEMENT_CLASS(Texture2D, true, Texture)
|
MTS_IMPLEMENT_CLASS(Texture2D, true, Texture)
|
||||||
MTS_NAMESPACE_END
|
MTS_NAMESPACE_END
|
||||||
|
|
|
@ -276,7 +276,8 @@ public:
|
||||||
Sampler *sampler) const {
|
Sampler *sampler) const {
|
||||||
Float rand = sampler->next1D(), sampledDistance;
|
Float rand = sampler->next1D(), sampledDistance;
|
||||||
Float samplingDensity = m_samplingDensity;
|
Float samplingDensity = m_samplingDensity;
|
||||||
if (rand <= m_mediumSamplingWeight) {
|
|
||||||
|
if (rand < m_mediumSamplingWeight) {
|
||||||
rand /= m_mediumSamplingWeight;
|
rand /= m_mediumSamplingWeight;
|
||||||
if (m_strategy != EMaximum) {
|
if (m_strategy != EMaximum) {
|
||||||
/* Choose the sampling density to be used */
|
/* Choose the sampling density to be used */
|
||||||
|
|
|
@ -258,7 +258,7 @@ int mitsuba_app(int argc, char **argv) {
|
||||||
/* Configure the scheduling subsystem */
|
/* Configure the scheduling subsystem */
|
||||||
Scheduler *scheduler = Scheduler::getInstance();
|
Scheduler *scheduler = Scheduler::getInstance();
|
||||||
for (int i=0; i<nprocs; ++i)
|
for (int i=0; i<nprocs; ++i)
|
||||||
scheduler->registerWorker(new LocalWorker(formatString("wrk%i", i)));
|
scheduler->registerWorker(new LocalWorker(i, formatString("wrk%i", i)));
|
||||||
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
||||||
|
|
||||||
/* Establish network connections to nested servers */
|
/* Establish network connections to nested servers */
|
||||||
|
|
|
@ -219,7 +219,7 @@ int mtssrv(int argc, char **argv) {
|
||||||
/* Configure the scheduling subsystem */
|
/* Configure the scheduling subsystem */
|
||||||
Scheduler *scheduler = Scheduler::getInstance();
|
Scheduler *scheduler = Scheduler::getInstance();
|
||||||
for (int i=0; i<nprocs; ++i)
|
for (int i=0; i<nprocs; ++i)
|
||||||
scheduler->registerWorker(new LocalWorker(formatString("wrk%i", i)));
|
scheduler->registerWorker(new LocalWorker(i, formatString("wrk%i", i)));
|
||||||
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
||||||
|
|
||||||
/* Establish network connections to nested servers */
|
/* Establish network connections to nested servers */
|
||||||
|
|
|
@ -234,7 +234,7 @@ int mtsutil(int argc, char **argv) {
|
||||||
/* Configure the scheduling subsystem */
|
/* Configure the scheduling subsystem */
|
||||||
Scheduler *scheduler = Scheduler::getInstance();
|
Scheduler *scheduler = Scheduler::getInstance();
|
||||||
for (int i=0; i<nprocs; ++i)
|
for (int i=0; i<nprocs; ++i)
|
||||||
scheduler->registerWorker(new LocalWorker(formatString("wrk%i", i)));
|
scheduler->registerWorker(new LocalWorker(i, formatString("wrk%i", i)));
|
||||||
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
std::vector<std::string> hosts = tokenize(networkHosts, ";");
|
||||||
|
|
||||||
/* Establish network connections to nested servers */
|
/* Establish network connections to nested servers */
|
||||||
|
|
|
@ -266,7 +266,7 @@ void MainWindow::initWorkers() {
|
||||||
m_workerPriority = (Thread::EThreadPriority)
|
m_workerPriority = (Thread::EThreadPriority)
|
||||||
settings.value("workerPriority", (int) Thread::ELowPriority).toInt();
|
settings.value("workerPriority", (int) Thread::ELowPriority).toInt();
|
||||||
for (int i=0; i<localWorkerCount; ++i)
|
for (int i=0; i<localWorkerCount; ++i)
|
||||||
scheduler->registerWorker(new LocalWorker(formatString("wrk%i", localWorkerCtr++), m_workerPriority));
|
scheduler->registerWorker(new LocalWorker(i, formatString("wrk%i", localWorkerCtr++), m_workerPriority));
|
||||||
|
|
||||||
int networkConnections = 0;
|
int networkConnections = 0;
|
||||||
QList<QVariant> connectionData = settings.value("connections").toList();
|
QList<QVariant> connectionData = settings.value("connections").toList();
|
||||||
|
@ -314,7 +314,7 @@ void MainWindow::initWorkers() {
|
||||||
QMessageBox::warning(this, tr("Scheduler warning"),
|
QMessageBox::warning(this, tr("Scheduler warning"),
|
||||||
tr("There must be at least one worker thread -- forcing creation of one."),
|
tr("There must be at least one worker thread -- forcing creation of one."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
scheduler->registerWorker(new LocalWorker(formatString("wrk%i", localWorkerCtr++), m_workerPriority));
|
scheduler->registerWorker(new LocalWorker(0, formatString("wrk%i", localWorkerCtr++), m_workerPriority));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList args = qApp->arguments();
|
QStringList args = qApp->arguments();
|
||||||
|
@ -1312,7 +1312,8 @@ void MainWindow::on_actionSettings_triggered() {
|
||||||
ref<Scheduler> sched = Scheduler::getInstance();
|
ref<Scheduler> sched = Scheduler::getInstance();
|
||||||
sched->pause();
|
sched->pause();
|
||||||
while (d.getLocalWorkerCount() > (int) localWorkers.size()) {
|
while (d.getLocalWorkerCount() > (int) localWorkers.size()) {
|
||||||
LocalWorker *worker = new LocalWorker(formatString("wrk%i", localWorkerCtr++), m_workerPriority);
|
LocalWorker *worker = new LocalWorker(localWorkerCtr, formatString("wrk%i", localWorkerCtr), m_workerPriority);
|
||||||
|
localWorkerCtr++;
|
||||||
sched->registerWorker(worker);
|
sched->registerWorker(worker);
|
||||||
localWorkers.push_back(worker);
|
localWorkers.push_back(worker);
|
||||||
}
|
}
|
||||||
|
@ -1726,6 +1727,9 @@ void MainWindow::onWorkBegin(const RenderJob *job, const RectangularWorkUnit *wu
|
||||||
void MainWindow::drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit &vwu) {
|
void MainWindow::drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit &vwu) {
|
||||||
int ox = vwu.offset.x, oy = vwu.offset.y,
|
int ox = vwu.offset.x, oy = vwu.offset.y,
|
||||||
ex = ox + vwu.size.x, ey = oy + vwu.size.y;
|
ex = ox + vwu.size.x, ey = oy + vwu.size.y;
|
||||||
|
if (vwu.size.x < 3 || vwu.size.y < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
const float *color = NULL;
|
const float *color = NULL;
|
||||||
|
|
||||||
/* Use desaturated colors to highlight the host
|
/* Use desaturated colors to highlight the host
|
||||||
|
@ -1753,17 +1757,22 @@ void MainWindow::drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vwu.size.x < 3 || vwu.size.y < 3)
|
float scale = .7f * (color[0] * 0.212671f + color[1] * 0.715160f + color[2] * 0.072169f);
|
||||||
return;
|
|
||||||
|
|
||||||
drawHLine(context, ox, oy, ox + 3, color);
|
float ncol[3] = {
|
||||||
drawHLine(context, ex - 4, oy, ex - 1, color);
|
color[0]*scale,
|
||||||
drawHLine(context, ox, ey - 1, ox + 3, color);
|
color[1]*scale,
|
||||||
drawHLine(context, ex - 4, ey - 1, ex - 1, color);
|
color[2]*scale
|
||||||
drawVLine(context, ox, oy, oy + 3, color);
|
};
|
||||||
drawVLine(context, ex - 1, oy, oy + 3, color);
|
|
||||||
drawVLine(context, ex - 1, ey - 4, ey - 1, color);
|
drawHLine(context, ox, oy, ox + 3, ncol);
|
||||||
drawVLine(context, ox, ey - 4, ey - 1, color);
|
drawHLine(context, ex - 4, oy, ex - 1, ncol);
|
||||||
|
drawHLine(context, ox, ey - 1, ox + 3, ncol);
|
||||||
|
drawHLine(context, ex - 4, ey - 1, ex - 1, ncol);
|
||||||
|
drawVLine(context, ox, oy, oy + 3, ncol);
|
||||||
|
drawVLine(context, ex - 1, oy, oy + 3, ncol);
|
||||||
|
drawVLine(context, ex - 1, ey - 4, ey - 1, ncol);
|
||||||
|
drawVLine(context, ox, ey - 4, ey - 1, ncol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onWorkCanceled(const RenderJob *job, const Point2i &offset, const Vector2i &size) {
|
void MainWindow::onWorkCanceled(const RenderJob *job, const Point2i &offset, const Vector2i &size) {
|
||||||
|
|
|
@ -385,7 +385,7 @@ void RenderSettingsDialog::apply(SceneContext *ctx) {
|
||||||
filmProps.setInteger("width", size.x, false);
|
filmProps.setInteger("width", size.x, false);
|
||||||
filmProps.setInteger("height", size.y, false);
|
filmProps.setInteger("height", size.y, false);
|
||||||
|
|
||||||
if (size.x != cropSize.x || size.y != cropSize.y) {
|
if (size.x != cropSize.x || size.y != cropSize.y || cropOffset.x != 0 || cropOffset.y != 0) {
|
||||||
filmProps.setInteger("cropWidth", cropSize.x, false);
|
filmProps.setInteger("cropWidth", cropSize.x, false);
|
||||||
filmProps.setInteger("cropHeight", cropSize.y, false);
|
filmProps.setInteger("cropHeight", cropSize.y, false);
|
||||||
filmProps.setInteger("cropOffsetX", cropOffset.x, false);
|
filmProps.setInteger("cropOffsetX", cropOffset.x, false);
|
||||||
|
|
|
@ -231,6 +231,16 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
|
||||||
setProperties(ctx->doc, rfilter,
|
setProperties(ctx->doc, rfilter,
|
||||||
ctx->scene->getFilm()->getReconstructionFilter()->getProperties());
|
ctx->scene->getFilm()->getReconstructionFilter()->getProperties());
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// Serialize medium references of the sensor
|
||||||
|
// ====================================================================
|
||||||
|
|
||||||
|
QList<QDomElement> oldSensorReferences = findAllChildren(oldSensor, "ref");
|
||||||
|
oldSensorReferences.append(findAllChildren(oldSensor, "medium"));
|
||||||
|
|
||||||
|
for (int i=0; i<oldSensorReferences.size(); ++i)
|
||||||
|
sensor.appendChild(ctx->doc.importNode(oldSensorReferences[i], true));
|
||||||
|
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
// Serialize the integrator configuration
|
// Serialize the integrator configuration
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
|
@ -3,9 +3,10 @@ uniform float multiplier;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture2D(source, gl_TexCoord[0].xy);
|
vec4 color = texture2D(source, gl_TexCoord[0].xy);
|
||||||
float luminance = multiplier * (color.r * 0.212671 + color.g * 0.715160 + color.b * 0.072169);
|
float luminance = color.r * 0.212671 + color.g * 0.715160 + color.b * 0.072169;
|
||||||
if (luminance < 0.0 || luminance != luminance || luminance == 1024.0)
|
if (luminance < 0.0 || luminance != luminance || luminance == 1024.0 || abs(luminance-.7) < 1e-4)
|
||||||
luminance = 0.0; // catch NaNs, negatives, and the Mitsuba banner
|
luminance = 0.0; // catch NaNs, negatives, and the Mitsuba banner
|
||||||
|
luminance = luminance * multiplier;
|
||||||
float logLuminance = log(1e-3 + luminance);
|
float logLuminance = log(1e-3 + luminance);
|
||||||
gl_FragColor = vec4(logLuminance, luminance, 0.0, 1.0);
|
gl_FragColor = vec4(logLuminance, luminance, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ add_shape(cube cube.cpp)
|
||||||
add_shape(hair hair.h hair.cpp)
|
add_shape(hair hair.h hair.cpp)
|
||||||
add_shape(shapegroup shapegroup.h shapegroup.cpp)
|
add_shape(shapegroup shapegroup.h shapegroup.cpp)
|
||||||
add_shape(instance instance.h instance.cpp)
|
add_shape(instance instance.h instance.cpp)
|
||||||
|
add_shape(heightfield heightfield.cpp)
|
||||||
#add_shape(deformable deformable.cpp)
|
#add_shape(deformable deformable.cpp)
|
||||||
add_shape(ply ply.cpp ply/ply_parser.cpp
|
add_shape(ply ply.cpp ply/ply_parser.cpp
|
||||||
ply/byte_order.hpp ply/config.hpp ply/io_operators.hpp
|
ply/byte_order.hpp ply/config.hpp ply/io_operators.hpp
|
||||||
|
|
|
@ -12,6 +12,7 @@ plugins += env.SharedLibrary('hair', ['hair.cpp'])
|
||||||
plugins += env.SharedLibrary('shapegroup', ['shapegroup.cpp'])
|
plugins += env.SharedLibrary('shapegroup', ['shapegroup.cpp'])
|
||||||
plugins += env.SharedLibrary('instance', ['instance.cpp'])
|
plugins += env.SharedLibrary('instance', ['instance.cpp'])
|
||||||
plugins += env.SharedLibrary('cube', ['cube.cpp'])
|
plugins += env.SharedLibrary('cube', ['cube.cpp'])
|
||||||
|
plugins += env.SharedLibrary('heightfield', ['heightfield.cpp'])
|
||||||
#plugins += env.SharedLibrary('deformable', ['deformable.cpp'])
|
#plugins += env.SharedLibrary('deformable', ['deformable.cpp'])
|
||||||
|
|
||||||
Export('plugins')
|
Export('plugins')
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
Vector dpdu = trafo(Vector(1, 0, 0));
|
Vector dpdu = trafo(Vector(1, 0, 0));
|
||||||
Vector dpdv = trafo(Vector(0, 1, 0));
|
Vector dpdv = trafo(Vector(0, 1, 0));
|
||||||
|
|
||||||
if (std::abs(dot(dpdu, dpdv)) > 1e-3f)
|
if (std::abs(dot(normalize(dpdu), normalize(dpdv))) > 1e-3f)
|
||||||
Log(EError, "Error: 'toWorld' transformation contains shear!");
|
Log(EError, "Error: 'toWorld' transformation contains shear!");
|
||||||
|
|
||||||
if (std::abs(dpdu.length() / dpdv.length() - 1) > 1e-3f)
|
if (std::abs(dpdu.length() / dpdv.length() - 1) > 1e-3f)
|
||||||
|
|
|
@ -0,0 +1,677 @@
|
||||||
|
/*
|
||||||
|
This file is part of Mitsuba, a physically based rendering system.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2011 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/render/shape.h>
|
||||||
|
#include <mitsuba/render/bsdf.h>
|
||||||
|
#include <mitsuba/render/emitter.h>
|
||||||
|
#include <mitsuba/render/medium.h>
|
||||||
|
#include <mitsuba/render/sensor.h>
|
||||||
|
#include <mitsuba/render/subsurface.h>
|
||||||
|
#include <mitsuba/render/trimesh.h>
|
||||||
|
#include <mitsuba/render/texture.h>
|
||||||
|
#include <mitsuba/core/bitmap.h>
|
||||||
|
#include <mitsuba/core/statistics.h>
|
||||||
|
#include <mitsuba/core/timer.h>
|
||||||
|
|
||||||
|
#define MTS_QTREE_MAXDEPTH 50
|
||||||
|
#define MTS_QTREE_FASTSTART 1
|
||||||
|
|
||||||
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static StatsCounter numTraversals("Height field", "Traversal operations per query", EAverage);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// Find the smallest t >= 0 such that a*t + b is a multiple of c
|
||||||
|
inline Float nextMultiple(Float a, Float b, Float c) {
|
||||||
|
Float tmp = b/c,
|
||||||
|
rounded = (a > 0 ? std::ceil(tmp) : std::floor(tmp)) * c,
|
||||||
|
diff = rounded - b;
|
||||||
|
|
||||||
|
if (diff == 0)
|
||||||
|
diff = math::signum(a) * c;
|
||||||
|
|
||||||
|
return diff / a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Temporary storage for patch-ray intersections
|
||||||
|
struct PatchIntersectionRecord {
|
||||||
|
Point p;
|
||||||
|
int x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Stack entry for recursive quadtree traversal
|
||||||
|
struct StackEntry {
|
||||||
|
int level, x, y;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class Heightfield : public Shape {
|
||||||
|
public:
|
||||||
|
Heightfield(const Properties &props) : Shape(props), m_data(NULL), m_normals(NULL), m_minmax(NULL) {
|
||||||
|
m_sizeHint = Vector2i(
|
||||||
|
props.getInteger("width", -1),
|
||||||
|
props.getInteger("height", -1)
|
||||||
|
);
|
||||||
|
|
||||||
|
m_objectToWorld = props.getTransform("toWorld", Transform());
|
||||||
|
m_shadingNormals = props.getBoolean("shadingNormals", true);
|
||||||
|
m_flipNormals = props.getBoolean("flipNormals", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightfield(Stream *stream, InstanceManager *manager)
|
||||||
|
: Shape(stream, manager), m_data(NULL), m_normals(NULL), m_minmax(NULL) {
|
||||||
|
|
||||||
|
m_objectToWorld = Transform(stream);
|
||||||
|
m_shadingNormals = stream->readBool();
|
||||||
|
m_flipNormals = stream->readBool();
|
||||||
|
m_dataSize = Vector2i(stream);
|
||||||
|
size_t size = (size_t) m_dataSize.x * (size_t) m_dataSize.y;
|
||||||
|
m_data = (Float *) allocAligned(size * sizeof(Float));
|
||||||
|
stream->readFloatArray(m_data, size);
|
||||||
|
buildInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Heightfield() {
|
||||||
|
if (m_data)
|
||||||
|
freeAligned(m_data);
|
||||||
|
if (m_minmax) {
|
||||||
|
for (int i=0; i<m_levelCount; ++i)
|
||||||
|
freeAligned(m_minmax[i]);
|
||||||
|
delete[] m_minmax;
|
||||||
|
delete[] m_levelSize;
|
||||||
|
delete[] m_numChildren;
|
||||||
|
delete[] m_blockSize;
|
||||||
|
delete[] m_blockSizeF;
|
||||||
|
}
|
||||||
|
if (m_normals)
|
||||||
|
freeAligned(m_normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||||
|
Shape::serialize(stream, manager);
|
||||||
|
m_objectToWorld.serialize(stream);
|
||||||
|
stream->writeBool(m_shadingNormals);
|
||||||
|
stream->writeBool(m_flipNormals);
|
||||||
|
m_dataSize.serialize(stream);
|
||||||
|
stream->writeFloatArray(m_data, (size_t) m_dataSize.x * (size_t) m_dataSize.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
AABB getAABB() const {
|
||||||
|
AABB result;
|
||||||
|
for (int i=0; i<8; ++i)
|
||||||
|
result.expandBy(m_objectToWorld(m_dataAABB.getCorner(i)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float getSurfaceArea() const {
|
||||||
|
return m_surfaceArea; /// XXX transformed surface area? ...
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getPrimitiveCount() const {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getEffectivePrimitiveCount() const {
|
||||||
|
return (size_t) m_levelSize[0].x * (size_t) m_levelSize[0].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *tmp) const {
|
||||||
|
StackEntry stack[MTS_QTREE_MAXDEPTH];
|
||||||
|
|
||||||
|
/* Transform ray into object space */
|
||||||
|
Ray ray;
|
||||||
|
m_objectToWorld.inverse()(_ray, ray);
|
||||||
|
|
||||||
|
/* Ray length to cross a single cell along the X or Y axis */
|
||||||
|
Float tDeltaXSingle = std::abs(ray.dRcp.x),
|
||||||
|
tDeltaYSingle = std::abs(ray.dRcp.y);
|
||||||
|
|
||||||
|
/* Cell coordinate increments for steps along the ray */
|
||||||
|
int iDeltaX = signumToInt(ray.d.x),
|
||||||
|
iDeltaY = signumToInt(ray.d.y);
|
||||||
|
|
||||||
|
int stackIdx = 0;
|
||||||
|
|
||||||
|
#if MTS_QTREE_FASTSTART
|
||||||
|
/* If the entire ray is restricted to a subtree of the quadtree,
|
||||||
|
directly start the traversal from the there instead of the root
|
||||||
|
node. This can save some unnecessary work. */
|
||||||
|
{
|
||||||
|
Point enterPt, exitPt;
|
||||||
|
Float nearT = mint, farT = maxt;
|
||||||
|
if (!m_dataAABB.rayIntersect(ray, nearT, farT, enterPt, exitPt))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Determine minima and maxima in integer coordinates (round down!) */
|
||||||
|
int minX = (int) std::min(enterPt.x, exitPt.x),
|
||||||
|
maxX = (int) std::max(enterPt.x, exitPt.x),
|
||||||
|
minY = (int) std::min(enterPt.y, exitPt.y),
|
||||||
|
maxY = (int) std::max(enterPt.y, exitPt.y);
|
||||||
|
|
||||||
|
/* Determine quadtree level */
|
||||||
|
int level = clamp(1 + log2i(
|
||||||
|
std::max((uint32_t) (minX ^ maxX), (uint32_t) (minY ^ maxY))),
|
||||||
|
0, m_levelCount-1);
|
||||||
|
|
||||||
|
/* Compute X and Y coordinates at that level */
|
||||||
|
const Vector2i &blockSize = m_blockSize[level];
|
||||||
|
int x = clamp(minX / blockSize.x, 0, m_levelSize[level].x-1),
|
||||||
|
y = clamp(minY / blockSize.y, 0, m_levelSize[level].y-1);
|
||||||
|
|
||||||
|
stack[stackIdx].level = level;
|
||||||
|
stack[stackIdx].x = x;
|
||||||
|
stack[stackIdx].y = y;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Start traversal from the root node of the quadtree */
|
||||||
|
stack[stackIdx].level = m_levelCount-1;
|
||||||
|
stack[stackIdx].x = 0;
|
||||||
|
stack[stackIdx].y = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
numTraversals.incrementBase();
|
||||||
|
|
||||||
|
size_t nTraversals = 0;
|
||||||
|
while (stackIdx >= 0) {
|
||||||
|
++nTraversals;
|
||||||
|
|
||||||
|
/* Pop a node from the stack and compute its bounding box */
|
||||||
|
StackEntry entry = stack[stackIdx--];
|
||||||
|
const Interval &interval = m_minmax[entry.level][
|
||||||
|
entry.x + entry.y * m_levelSize[entry.level].x];
|
||||||
|
const Vector2 &blockSize = m_blockSizeF[entry.level];
|
||||||
|
AABB aabb(
|
||||||
|
Point3(0, 0, interval.min),
|
||||||
|
Point3(blockSize.x, blockSize.y, interval.max)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Intersect the ray against the bounding box, in local coordinates */
|
||||||
|
Ray localRay(Point(ray.o.x - entry.x*blockSize.x,
|
||||||
|
ray.o.y - entry.y*blockSize.y, ray.o.z), ray.d, 0);
|
||||||
|
Float nearT = mint, farT = maxt;
|
||||||
|
Point enterPt, exitPt;
|
||||||
|
|
||||||
|
if (!aabb.rayIntersect(localRay, nearT, farT, enterPt, exitPt)) {
|
||||||
|
/* The bounding box was not intersected -- skip */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float tMax = farT - nearT;
|
||||||
|
|
||||||
|
if (entry.level > 0) {
|
||||||
|
/* Inner node -- push child nodes in 2D DDA order */
|
||||||
|
const Vector2i &numChildren = m_numChildren[entry.level];
|
||||||
|
const Vector2 &subBlockSize = m_blockSizeF[--entry.level];
|
||||||
|
entry.x *= numChildren.x; entry.y *= numChildren.y;
|
||||||
|
|
||||||
|
int x = (exitPt.x >= subBlockSize.x) ? numChildren.x-1 : 0;
|
||||||
|
int y = (exitPt.y >= subBlockSize.y) ? numChildren.y-1 : 0;
|
||||||
|
|
||||||
|
Float tDeltaX = tDeltaXSingle * subBlockSize.x,
|
||||||
|
tDeltaY = tDeltaYSingle * subBlockSize.y,
|
||||||
|
tNextX = nextMultiple(-ray.d.x, exitPt.x, subBlockSize.x),
|
||||||
|
tNextY = nextMultiple(-ray.d.y, exitPt.y, subBlockSize.y),
|
||||||
|
t = 0;
|
||||||
|
|
||||||
|
while ((uint32_t) x < (uint32_t) numChildren.x &&
|
||||||
|
(uint32_t) y < (uint32_t) numChildren.y && t <= tMax) {
|
||||||
|
stack[++stackIdx].level = entry.level;
|
||||||
|
stack[stackIdx].x = entry.x + x;
|
||||||
|
stack[stackIdx].y = entry.y + y;
|
||||||
|
|
||||||
|
if (tNextX < tNextY) {
|
||||||
|
t = tNextX;
|
||||||
|
tNextX += tDeltaX;
|
||||||
|
x -= iDeltaX;
|
||||||
|
} else {
|
||||||
|
t = tNextY;
|
||||||
|
tNextY += tDeltaY;
|
||||||
|
y -= iDeltaY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Intersect the ray against a bilinear patch */
|
||||||
|
Float
|
||||||
|
f00 = m_data[entry.y * m_dataSize.x + entry.x],
|
||||||
|
f01 = m_data[(entry.y + 1) * m_dataSize.x + entry.x],
|
||||||
|
f10 = m_data[entry.y * m_dataSize.x + entry.x + 1],
|
||||||
|
f11 = m_data[(entry.y + 1) * m_dataSize.x + entry.x + 1];
|
||||||
|
|
||||||
|
Float A = ray.d.x * ray.d.y * (f00 - f01 - f10 + f11);
|
||||||
|
Float B = ray.d.y * (f01 - f00 + enterPt.x * (f00 - f01 - f10 + f11))
|
||||||
|
+ ray.d.x * (f10 - f00 + enterPt.y * (f00 - f01 - f10 + f11))
|
||||||
|
- ray.d.z;
|
||||||
|
Float C = (enterPt.x - 1) * (enterPt.y - 1) * f00
|
||||||
|
+ enterPt.y * f01 + enterPt.x * (f10 - enterPt.y * (f01 + f10 - f11))
|
||||||
|
- enterPt.z;
|
||||||
|
|
||||||
|
Float t0, t1;
|
||||||
|
if (!solveQuadratic(A, B, C, t0, t1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Float min = std::max(-Epsilon, mint - nearT);
|
||||||
|
Float max = std::min(tMax + Epsilon, maxt - nearT);
|
||||||
|
|
||||||
|
if (t0 >= min && t0 <= max)
|
||||||
|
t = t0;
|
||||||
|
else if (t1 >= min && t1 <= max)
|
||||||
|
t = t1;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tmp) {
|
||||||
|
PatchIntersectionRecord &temp = *((PatchIntersectionRecord *) tmp);
|
||||||
|
Point pLocal = enterPt + ray.d * t;
|
||||||
|
temp.x = entry.x;
|
||||||
|
temp.y = entry.y;
|
||||||
|
temp.p = pLocal;
|
||||||
|
t += nearT;
|
||||||
|
}
|
||||||
|
numTraversals += nTraversals;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numTraversals += nTraversals;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillIntersectionRecord(const Ray &ray,
|
||||||
|
const void *tmp, Intersection &its) const {
|
||||||
|
PatchIntersectionRecord &temp = *((PatchIntersectionRecord *) tmp);
|
||||||
|
|
||||||
|
int x = temp.x, y = temp.y, width = m_dataSize.x;
|
||||||
|
Float
|
||||||
|
f00 = m_data[y * width + x],
|
||||||
|
f01 = m_data[(y+1) * width + x],
|
||||||
|
f10 = m_data[y * width + x + 1],
|
||||||
|
f11 = m_data[(y+1) * width + x + 1];
|
||||||
|
|
||||||
|
Point pLocal(temp.p.x + temp.x, temp.p.y + temp.y, temp.p.z);
|
||||||
|
its.uv = Point2(pLocal.x * m_invSize.x, pLocal.y * m_invSize.y);
|
||||||
|
its.p = m_objectToWorld(pLocal);
|
||||||
|
its.dpdu = m_objectToWorld(Vector(1, 0,
|
||||||
|
(1.0f - temp.p.y) * (f10 - f00) + temp.p.y * (f11 - f01)) * m_levelSize[0].x);
|
||||||
|
its.dpdv = m_objectToWorld(Vector(0, 1,
|
||||||
|
(1.0f - temp.p.x) * (f01 - f00) + temp.p.x * (f11 - f10)) * m_levelSize[0].y);
|
||||||
|
|
||||||
|
its.geoFrame.s = normalize(its.dpdu);
|
||||||
|
its.geoFrame.t = normalize(its.dpdv - dot(its.dpdv, its.geoFrame.s) * its.geoFrame.s);
|
||||||
|
its.geoFrame.n = cross(its.geoFrame.s, its.geoFrame.t);
|
||||||
|
|
||||||
|
if (m_shadingNormals) {
|
||||||
|
const Normal
|
||||||
|
&n00 = m_normals[y * width + x],
|
||||||
|
&n01 = m_normals[(y+1) * width + x],
|
||||||
|
&n10 = m_normals[y * width + x + 1],
|
||||||
|
&n11 = m_normals[(y+1) * width + x + 1];
|
||||||
|
|
||||||
|
its.shFrame.n = normalize(m_objectToWorld(Normal(
|
||||||
|
(1 - temp.p.x) * ((1-temp.p.y) * n00 + temp.p.y * n01)
|
||||||
|
+ temp.p.x * ((1-temp.p.y) * n10 + temp.p.y * n11))));
|
||||||
|
|
||||||
|
its.shFrame.s = normalize(its.geoFrame.s - dot(its.geoFrame.s, its.shFrame.n) * its.shFrame.n);
|
||||||
|
its.shFrame.t = cross(its.shFrame.n, its.shFrame.s);
|
||||||
|
} else {
|
||||||
|
its.shFrame = its.geoFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_flipNormals) {
|
||||||
|
its.shFrame.n *= -1;
|
||||||
|
its.geoFrame.n *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
its.shape = this;
|
||||||
|
its.wi = its.toLocal(-ray.d);
|
||||||
|
its.hasUVPartials = false;
|
||||||
|
its.instance = NULL;
|
||||||
|
its.time = ray.time;
|
||||||
|
its.primIndex = x + y*width;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rayIntersect(const Ray &ray, Float mint, Float maxt) const {
|
||||||
|
Float t;
|
||||||
|
return rayIntersect(ray, mint, maxt, t, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getNormalDerivative(const Intersection &its,
|
||||||
|
Vector &dndu, Vector &dndv, bool shadingFrame) const {
|
||||||
|
int width = m_dataSize.x,
|
||||||
|
x = its.primIndex % width,
|
||||||
|
y = its.primIndex / width;
|
||||||
|
|
||||||
|
Float u = its.uv.x * m_levelSize[0].x - x;
|
||||||
|
Float v = its.uv.y * m_levelSize[0].y - y;
|
||||||
|
|
||||||
|
Normal normal;
|
||||||
|
if (shadingFrame && m_shadingNormals) {
|
||||||
|
/* Derivatives for bilinear patch with interpolated shading normals */
|
||||||
|
const Normal
|
||||||
|
&n00 = m_normals[y * width + x],
|
||||||
|
&n01 = m_normals[(y+1) * width + x],
|
||||||
|
&n10 = m_normals[y * width + x + 1],
|
||||||
|
&n11 = m_normals[(y+1) * width + x + 1];
|
||||||
|
|
||||||
|
normal = m_objectToWorld(Normal(
|
||||||
|
(1 - u) * ((1-v) * n00 + v * n01)
|
||||||
|
+ u * ((1-v) * n10 + v * n11)));
|
||||||
|
|
||||||
|
dndu = m_objectToWorld(Normal((1.0f - v) * (n10 - n00) + v * (n11 - n01))) * m_levelSize[0].x;
|
||||||
|
dndv = m_objectToWorld(Normal((1.0f - u) * (n01 - n00) + u * (n11 - n10))) * m_levelSize[0].y;
|
||||||
|
} else {
|
||||||
|
/* Derivatives for bilinear patch with geometric normals */
|
||||||
|
Float
|
||||||
|
f00 = m_data[y * width + x],
|
||||||
|
f01 = m_data[(y+1) * width + x],
|
||||||
|
f10 = m_data[y * width + x + 1],
|
||||||
|
f11 = m_data[(y+1) * width + x + 1];
|
||||||
|
|
||||||
|
normal = m_objectToWorld(
|
||||||
|
Normal(f00 - f10 + (f01 + f10 - f00 - f11)*v,
|
||||||
|
f00 - f01 + (f01 + f10 - f00 - f11)*u, 1));
|
||||||
|
|
||||||
|
dndu = m_objectToWorld(Normal(0, f01 + f10 - f00 - f11, 0)) * m_levelSize[0].x;
|
||||||
|
dndv = m_objectToWorld(Normal(f01 + f10 - f00 - f11, 0, 0)) * m_levelSize[0].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Account for normalization */
|
||||||
|
Float invLength = 1/normal.length();
|
||||||
|
|
||||||
|
normal *= invLength;
|
||||||
|
dndu *= invLength;
|
||||||
|
dndv *= invLength;
|
||||||
|
|
||||||
|
dndu -= dot(normal, dndu) * normal;
|
||||||
|
dndv -= dot(normal, dndv) * normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||||
|
const Class *cClass = child->getClass();
|
||||||
|
if (cClass->derivesFrom(Texture::m_theClass)) {
|
||||||
|
if (m_data != NULL)
|
||||||
|
Log(EError, "Attempted to attach multiple textures to a height field shape!");
|
||||||
|
|
||||||
|
ref<Bitmap> bitmap = static_cast<Texture *>(child)->getBitmap(m_sizeHint);
|
||||||
|
|
||||||
|
m_dataSize = bitmap->getSize();
|
||||||
|
if (m_dataSize.x < 2) m_dataSize.x = 2;
|
||||||
|
if (m_dataSize.y < 2) m_dataSize.y = 2;
|
||||||
|
if (!isPowerOfTwo(m_dataSize.x - 1)) m_dataSize.x = (int) roundToPowerOfTwo((uint32_t) m_dataSize.x - 1) + 1;
|
||||||
|
if (!isPowerOfTwo(m_dataSize.y - 1)) m_dataSize.y = (int) roundToPowerOfTwo((uint32_t) m_dataSize.y - 1) + 1;
|
||||||
|
|
||||||
|
if (bitmap->getSize() != m_dataSize) {
|
||||||
|
Log(EInfo, "Resampling heightfield texture from %ix%i to %ix%i ..",
|
||||||
|
bitmap->getWidth(), bitmap->getHeight(), m_dataSize.x, m_dataSize.y);
|
||||||
|
|
||||||
|
bitmap = bitmap->resample(NULL, ReconstructionFilter::EClamp,
|
||||||
|
ReconstructionFilter::EClamp, m_dataSize,
|
||||||
|
-std::numeric_limits<Float>::infinity(),
|
||||||
|
std::numeric_limits<Float>::infinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = (size_t) m_dataSize.x * (size_t) m_dataSize.y * sizeof(Float);
|
||||||
|
m_data = (Float *) allocAligned(size);
|
||||||
|
bitmap->convert(m_data, Bitmap::ELuminance, Bitmap::EFloat);
|
||||||
|
|
||||||
|
m_objectToWorld = m_objectToWorld * Transform::translate(Vector(-1, -1, 0)) * Transform::scale(Vector(
|
||||||
|
(Float) 2 / (m_dataSize.x-1),
|
||||||
|
(Float) 2 / (m_dataSize.y-1), 1));
|
||||||
|
|
||||||
|
buildInternal();
|
||||||
|
} else {
|
||||||
|
Shape::addChild(name, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildInternal() {
|
||||||
|
size_t storageSize = (size_t) m_dataSize.x * (size_t) m_dataSize.y * sizeof(Float);
|
||||||
|
Log(EInfo, "Building acceleration data structure for %ix%i height field ..", m_dataSize.x, m_dataSize.y);
|
||||||
|
|
||||||
|
ref<Timer> timer = new Timer();
|
||||||
|
m_levelCount = (int) std::max(log2i((uint32_t) m_dataSize.x-1), log2i((uint32_t) m_dataSize.y-1)) + 1;
|
||||||
|
|
||||||
|
m_levelSize = new Vector2i[m_levelCount];
|
||||||
|
m_numChildren = new Vector2i[m_levelCount];
|
||||||
|
m_blockSize = new Vector2i[m_levelCount];
|
||||||
|
m_blockSizeF = new Vector2[m_levelCount];
|
||||||
|
m_minmax = new Interval*[m_levelCount];
|
||||||
|
|
||||||
|
m_levelSize[0] = Vector2i(m_dataSize.x - 1, m_dataSize.y - 1);
|
||||||
|
m_blockSize[0] = Vector2i(1, 1);
|
||||||
|
m_blockSizeF[0] = Vector2(1, 1);
|
||||||
|
m_invSize = Vector2((Float) 1 / m_levelSize[0].x, (Float) 1 / m_levelSize[0].y);
|
||||||
|
m_surfaceArea = 0;
|
||||||
|
size_t size = (size_t) m_levelSize[0].x * (size_t) m_levelSize[0].y * sizeof(Interval);
|
||||||
|
m_minmax[0] = (Interval *) allocAligned(size);
|
||||||
|
storageSize += size;
|
||||||
|
|
||||||
|
/* Build the lowest MIP layer directly from the heightfield data */
|
||||||
|
Interval *bounds = m_minmax[0];
|
||||||
|
for (int y=0; y<m_levelSize[0].y; ++y) {
|
||||||
|
for (int x=0; x<m_levelSize[0].x; ++x) {
|
||||||
|
Float f00 = m_data[y * m_dataSize.x + x];
|
||||||
|
Float f10 = m_data[y * m_dataSize.x + x + 1];
|
||||||
|
Float f01 = m_data[(y + 1) * m_dataSize.x + x];
|
||||||
|
Float f11 = m_data[(y + 1) * m_dataSize.x + x + 1];
|
||||||
|
Float fmin = std::min(std::min(f00, f01), std::min(f10, f11));
|
||||||
|
Float fmax = std::max(std::max(f00, f01), std::max(f10, f11));
|
||||||
|
*bounds++ = Interval(fmin, fmax);
|
||||||
|
|
||||||
|
/* Estimate the total surface area (this is approximate) */
|
||||||
|
Float diff0 = f01-f10, diff1 = f00-f11;
|
||||||
|
m_surfaceArea += std::sqrt(1.0f + .5f * (diff0*diff0 + diff1*diff1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Propagate height bounds upwards to the other layers */
|
||||||
|
for (int level=1; level<m_levelCount; ++level) {
|
||||||
|
Vector2i &cur = m_levelSize[level],
|
||||||
|
&prev = m_levelSize[level-1];
|
||||||
|
|
||||||
|
/* Calculate size of this layer */
|
||||||
|
cur.x = prev.x > 1 ? (prev.x / 2) : 1;
|
||||||
|
cur.y = prev.y > 1 ? (prev.y / 2) : 1;
|
||||||
|
|
||||||
|
m_numChildren[level].x = prev.x > 1 ? 2 : 1;
|
||||||
|
m_numChildren[level].y = prev.y > 1 ? 2 : 1;
|
||||||
|
m_blockSize[level] = Vector2i(
|
||||||
|
m_levelSize[0].x / cur.x,
|
||||||
|
m_levelSize[0].y / cur.y
|
||||||
|
);
|
||||||
|
m_blockSizeF[level] = Vector2(m_blockSize[level]);
|
||||||
|
|
||||||
|
/* Allocate memory for interval data */
|
||||||
|
Interval *prevBounds = m_minmax[level-1], *curBounds;
|
||||||
|
size_t size = (size_t) cur.x * (size_t) cur.y * sizeof(Interval);
|
||||||
|
m_minmax[level] = curBounds = (Interval *) allocAligned(size);
|
||||||
|
storageSize += size;
|
||||||
|
|
||||||
|
/* Build by querying the previous layer */
|
||||||
|
for (int y=0; y<cur.y; ++y) {
|
||||||
|
int y0 = std::min(2*y, prev.y-1),
|
||||||
|
y1 = std::min(2*y+1, prev.y-1);
|
||||||
|
for (int x=0; x<cur.x; ++x) {
|
||||||
|
int x0 = std::min(2*x, prev.x-1),
|
||||||
|
x1 = std::min(2*x+1, prev.x-1);
|
||||||
|
const Interval &f00 = prevBounds[y0 * prev.x + x0], &f01 = prevBounds[y0 * prev.x + x1];
|
||||||
|
const Interval &f10 = prevBounds[y1 * prev.x + x0], &f11 = prevBounds[y1 * prev.x + x1];
|
||||||
|
Interval combined(f00);
|
||||||
|
combined.expandBy(f01);
|
||||||
|
combined.expandBy(f10);
|
||||||
|
combined.expandBy(f11);
|
||||||
|
*curBounds++ = combined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_shadingNormals) {
|
||||||
|
Log(EInfo, "Precomputing shading normals ..");
|
||||||
|
size_t size = (size_t) m_dataSize.x * (size_t) m_dataSize.y * sizeof(Normal);
|
||||||
|
m_normals = (Normal *) allocAligned(size);
|
||||||
|
memset(m_normals, 0, size);
|
||||||
|
storageSize += size;
|
||||||
|
|
||||||
|
for (int offset=0; offset<2; ++offset) {
|
||||||
|
#if defined(MTS_OPENMP)
|
||||||
|
#pragma omp parallel for
|
||||||
|
#endif
|
||||||
|
for (int y=offset; y<m_levelSize[0].y; y+=2) {
|
||||||
|
for (int x=0; x<m_levelSize[0].x; ++x) {
|
||||||
|
Float f00 = m_data[y * m_dataSize.x + x];
|
||||||
|
Float f10 = m_data[y * m_dataSize.x + x + 1];
|
||||||
|
Float f01 = m_data[(y + 1) * m_dataSize.x + x];
|
||||||
|
Float f11 = m_data[(y + 1) * m_dataSize.x + x + 1];
|
||||||
|
|
||||||
|
m_normals[y * m_dataSize.x + x] += normalize(Normal(f00 - f10, f00 - f01, 1));
|
||||||
|
m_normals[y * m_dataSize.x + x + 1] += normalize(Normal(f00 - f10, f10 - f11, 1));
|
||||||
|
m_normals[(y + 1) * m_dataSize.x + x] += normalize(Normal(f01 - f11, f00 - f01, 1));
|
||||||
|
m_normals[(y + 1) * m_dataSize.x + x + 1] += normalize(Normal(f01 - f11, f10 - f11, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MTS_OPENMP)
|
||||||
|
#pragma omp parallel for
|
||||||
|
#endif
|
||||||
|
for (int y=0; y<m_dataSize.y; ++y) {
|
||||||
|
for (int x=0; x<m_dataSize.x; ++x) {
|
||||||
|
Normal &normal = m_normals[x + y * m_dataSize.x];
|
||||||
|
normal /= normal.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(EInfo, "Done (took %i ms, uses %s of memory)", timer->getMilliseconds(),
|
||||||
|
memString(storageSize).c_str());
|
||||||
|
|
||||||
|
m_dataAABB = AABB(
|
||||||
|
Point3(0, 0, m_minmax[m_levelCount-1][0].min),
|
||||||
|
Point3(m_levelSize[0].x, m_levelSize[0].y, m_minmax[m_levelCount-1][0].max)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<TriMesh> createTriMesh() {
|
||||||
|
Vector2i size = m_dataSize;
|
||||||
|
|
||||||
|
/* Limit the size of the mesh */
|
||||||
|
while (size.x > 256 && size.y > 256) {
|
||||||
|
size.x = std::max(size.x / 2, 2);
|
||||||
|
size.y = std::max(size.y / 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t numTris = 2 * (size_t) (size.x-1) * (size_t) (size.y-1);
|
||||||
|
size_t numVertices = (size_t) size.x * (size_t) size.y;
|
||||||
|
|
||||||
|
ref<TriMesh> mesh = new TriMesh("Height field approximation",
|
||||||
|
numTris, numVertices, false, true, false, false, !m_shadingNormals);
|
||||||
|
|
||||||
|
Point *vertices = mesh->getVertexPositions();
|
||||||
|
Point2 *texcoords = mesh->getVertexTexcoords();
|
||||||
|
Triangle *triangles = mesh->getTriangles();
|
||||||
|
|
||||||
|
Float dx = (Float) 1 / (size.x - 1);
|
||||||
|
Float dy = (Float) 1 / (size.y - 1);
|
||||||
|
Float scaleX = (Float) m_dataSize.x / size.x;
|
||||||
|
Float scaleY = (Float) m_dataSize.y / size.y;
|
||||||
|
|
||||||
|
uint32_t vertexIdx = 0;
|
||||||
|
for (int y=0; y<size.y; ++y) {
|
||||||
|
int py = std::min((int) (scaleY * y), m_dataSize.y-1);
|
||||||
|
for (int x=0; x<size.x; ++x) {
|
||||||
|
int px = std::min((int) (scaleX * x), m_dataSize.x-1);
|
||||||
|
texcoords[vertexIdx] = Point2(x*dx, y*dy);
|
||||||
|
vertices[vertexIdx++] = m_objectToWorld(Point(px, py,
|
||||||
|
m_data[px + py*m_dataSize.x]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(vertexIdx == numVertices);
|
||||||
|
|
||||||
|
uint32_t triangleIdx = 0;
|
||||||
|
for (int y=1; y<size.y; ++y) {
|
||||||
|
for (int x=0; x<size.x-1; ++x) {
|
||||||
|
uint32_t nextx = x + 1;
|
||||||
|
uint32_t idx0 = size.x*y + x;
|
||||||
|
uint32_t idx1 = size.x*y + nextx;
|
||||||
|
uint32_t idx2 = size.x*(y-1) + x;
|
||||||
|
uint32_t idx3 = size.x*(y-1) + nextx;
|
||||||
|
|
||||||
|
triangles[triangleIdx].idx[0] = idx0;
|
||||||
|
triangles[triangleIdx].idx[1] = idx2;
|
||||||
|
triangles[triangleIdx].idx[2] = idx1;
|
||||||
|
triangleIdx++;
|
||||||
|
triangles[triangleIdx].idx[0] = idx1;
|
||||||
|
triangles[triangleIdx].idx[1] = idx2;
|
||||||
|
triangles[triangleIdx].idx[2] = idx3;
|
||||||
|
triangleIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(triangleIdx == numTris);
|
||||||
|
mesh->copyAttachments(this);
|
||||||
|
mesh->configure();
|
||||||
|
|
||||||
|
return mesh.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "HeightField[" << endl
|
||||||
|
<< " size = " << m_dataSize.toString() << "," << endl
|
||||||
|
<< " shadingNormals = " << m_shadingNormals << "," << endl
|
||||||
|
<< " flipNormals = " << m_flipNormals << "," << endl
|
||||||
|
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
|
||||||
|
<< " aabb = " << indent(getAABB().toString()) << "," << endl
|
||||||
|
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
|
||||||
|
if (isMediumTransition())
|
||||||
|
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
|
||||||
|
<< " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl;
|
||||||
|
oss << " emitter = " << indent(m_emitter.toString()) << "," << endl
|
||||||
|
<< " sensor = " << indent(m_sensor.toString()) << "," << endl
|
||||||
|
<< " subsurface = " << indent(m_subsurface.toString()) << endl
|
||||||
|
<< "]";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
MTS_DECLARE_CLASS()
|
||||||
|
private:
|
||||||
|
Transform m_objectToWorld;
|
||||||
|
Vector2i m_sizeHint;
|
||||||
|
AABB m_dataAABB;
|
||||||
|
bool m_shadingNormals;
|
||||||
|
bool m_flipNormals;
|
||||||
|
|
||||||
|
/* Height field data */
|
||||||
|
Float *m_data;
|
||||||
|
Normal *m_normals;
|
||||||
|
Vector2i m_dataSize;
|
||||||
|
Vector2 m_invSize;
|
||||||
|
Float m_surfaceArea;
|
||||||
|
|
||||||
|
/* Min-max quadtree data */
|
||||||
|
int m_levelCount;
|
||||||
|
Vector2i *m_levelSize;
|
||||||
|
Vector2i *m_numChildren;
|
||||||
|
Vector2i *m_blockSize;
|
||||||
|
Vector2 *m_blockSizeF;
|
||||||
|
Interval **m_minmax;
|
||||||
|
};
|
||||||
|
|
||||||
|
MTS_IMPLEMENT_CLASS_S(Heightfield, false, Shape)
|
||||||
|
MTS_EXPORT_PLUGIN(Heightfield, "Height field intersection primitive");
|
||||||
|
MTS_NAMESPACE_END
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
m_frame = Frame(normalize(m_dpdu), normalize(m_dpdv), normal);
|
m_frame = Frame(normalize(m_dpdu), normalize(m_dpdv), normal);
|
||||||
|
|
||||||
m_invSurfaceArea = 1.0f / getSurfaceArea();
|
m_invSurfaceArea = 1.0f / getSurfaceArea();
|
||||||
if (std::abs(dot(m_dpdu, m_dpdv)) > Epsilon)
|
if (std::abs(dot(normalize(m_dpdu), normalize(m_dpdv))) > Epsilon)
|
||||||
Log(EError, "Error: 'toWorld' transformation contains shear!");
|
Log(EError, "Error: 'toWorld' transformation contains shear!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -428,7 +428,7 @@ public:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<Bitmap> getBitmap() const {
|
ref<Bitmap> getBitmap(const Vector2i &/* unused */) const {
|
||||||
return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap();
|
return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
* contents by a user-specified value. This can be quite useful when a
|
* contents by a user-specified value. This can be quite useful when a
|
||||||
* texture is too dark or too bright. The plugin can also be used to adjust
|
* texture is too dark or too bright. The plugin can also be used to adjust
|
||||||
* the height of a bump map when using the \pluginref{bump} plugin.
|
* the height of a bump map when using the \pluginref{bump} plugin.
|
||||||
|
*
|
||||||
* \begin{xml}[caption=Scaling the contents of a bitmap texture]
|
* \begin{xml}[caption=Scaling the contents of a bitmap texture]
|
||||||
* <texture type="scale">
|
* <texture type="scale">
|
||||||
* <float name="scale" value="0.5"/>
|
* <float name="scale" value="0.5"/>
|
||||||
|
@ -46,7 +47,6 @@ MTS_NAMESPACE_BEGIN
|
||||||
* </texture>
|
* </texture>
|
||||||
* </texture>
|
* </texture>
|
||||||
* \end{xml}
|
* \end{xml}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ScalingTexture : public Texture {
|
class ScalingTexture : public Texture {
|
||||||
|
@ -102,6 +102,23 @@ public:
|
||||||
return m_nested->isConstant();
|
return m_nested->isConstant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> getBitmap(const Vector2i &sizeHint) const {
|
||||||
|
ref<Bitmap> result = m_nested->getBitmap(sizeHint);
|
||||||
|
|
||||||
|
if (m_scale == Spectrum(m_scale[0])) {
|
||||||
|
result->scale(m_scale[0]);
|
||||||
|
} else {
|
||||||
|
result = result->convert(Bitmap::ESpectrum, Bitmap::EFloat);
|
||||||
|
|
||||||
|
Spectrum *data = (Spectrum *) result->getFloatData();
|
||||||
|
size_t pixelCount = result->getPixelCount();
|
||||||
|
for (size_t i=0; i<pixelCount; ++i)
|
||||||
|
*data++ *= m_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "ScalingTexture[" << endl
|
oss << "ScalingTexture[" << endl
|
||||||
|
|
Loading…
Reference in New Issue