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'
|
||||
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
|
||||
vars = Variables(configFile)
|
||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
|||
env = locals()
|
||||
|
||||
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():
|
||||
if entry[:2] == '-I':
|
||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
|||
env = locals()
|
||||
|
||||
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():
|
||||
if entry[:2] == '-I':
|
||||
|
|
|
@ -31,7 +31,7 @@ pyver = os.popen("python --version 2>&1 | grep -oE '([[:digit:]].[[:digit:]])'")
|
|||
env = locals()
|
||||
|
||||
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():
|
||||
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="shape" type="shape"/>
|
||||
<xsd:element name="medium" type="medium"/>
|
||||
<xsd:element name="texture" type="texture"/>
|
||||
</xsd:choice>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
|
|
|
@ -153,7 +153,7 @@ scheduler = Scheduler.getInstance()
|
|||
|
||||
# Start up the scheduling system with one worker per local core
|
||||
for i in range(0, multiprocessing.cpu_count()):
|
||||
scheduler.registerWorker(LocalWorker('wrk%i' % i))
|
||||
scheduler.registerWorker(LocalWorker(i, 'wrk%i' % i))
|
||||
scheduler.start()
|
||||
|
||||
# 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
|
||||
* 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
|
||||
* \c nearT and \c farT values as a tuple (or \c None, when no
|
||||
* intersection was found)
|
||||
|
@ -302,11 +307,10 @@ template <typename T> struct TAABB {
|
|||
|
||||
/* For each pair of AABB planes */
|
||||
for (int i=0; i<PointType::dim; i++) {
|
||||
const Float direction = ray.d[i];
|
||||
const Float origin = ray.o[i];
|
||||
const Float minVal = min[i], maxVal = max[i];
|
||||
|
||||
if (direction == 0) {
|
||||
if (ray.d[i] == 0) {
|
||||
/* The ray is parallel to the planes */
|
||||
if (origin < minVal || origin > maxVal)
|
||||
return false;
|
||||
|
@ -329,6 +333,68 @@ template <typename T> struct TAABB {
|
|||
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
|
||||
inline void serialize(Stream *stream) const {
|
||||
min.serialize(stream);
|
||||
|
|
|
@ -270,6 +270,14 @@ public:
|
|||
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
|
||||
* the necessary amount of memory
|
||||
|
@ -287,9 +295,13 @@ public:
|
|||
* \param channelCount
|
||||
* Channel count of the image. This parameter is only required when
|
||||
* \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,
|
||||
int channelCount = -1);
|
||||
uint8_t channelCount = 0, uint8_t *data = NULL);
|
||||
|
||||
/**
|
||||
* \brief Load a bitmap from an arbitrary stream data source
|
||||
|
@ -335,7 +347,7 @@ public:
|
|||
inline int getHeight() const { return m_size.y; }
|
||||
|
||||
/// 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
|
||||
inline bool isSquare() const { return m_size.x == m_size.y; }
|
||||
|
@ -712,18 +724,13 @@ public:
|
|||
ref<Bitmap> rotateFlip(ERotateFlipType type) const;
|
||||
|
||||
/**
|
||||
* \brief Accumulate the contents of another bitmap into the
|
||||
* region of the specified offset
|
||||
* \brief Scale the entire image by a certain value
|
||||
*
|
||||
* 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.
|
||||
* Skips the image's alpha channel, if it has one. When the image uses
|
||||
* a fixed point representation and a pixel value overflows during the
|
||||
* scale operation, it is clamped to the representable range.
|
||||
*/
|
||||
void accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
||||
Point2i targetOffset, Vector2i size);
|
||||
void scale(Float value);
|
||||
|
||||
/**
|
||||
* \brief Color balancing: apply the given scale factors to the
|
||||
|
@ -743,6 +750,19 @@ public:
|
|||
*/
|
||||
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
|
||||
* region of the specified offset
|
||||
|
@ -760,6 +780,41 @@ public:
|
|||
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
|
||||
*
|
||||
|
@ -941,7 +996,8 @@ protected:
|
|||
Vector2i m_size;
|
||||
uint8_t *m_data;
|
||||
Float m_gamma;
|
||||
int m_channelCount;
|
||||
uint8_t m_channelCount;
|
||||
bool m_ownsData;
|
||||
Properties m_metadata;
|
||||
};
|
||||
|
||||
|
|
|
@ -149,6 +149,7 @@ typedef TVector2<size_t> Size2;
|
|||
typedef TVector3<size_t> Size3;
|
||||
typedef TVector4<size_t> Size4;
|
||||
typedef TAABB<Point1> AABB1;
|
||||
typedef AABB1 Interval;
|
||||
typedef TAABB<Point2> AABB2;
|
||||
typedef TAABB<Point4> AABB4;
|
||||
/// \ingroup libpython
|
||||
|
|
|
@ -173,6 +173,9 @@ template <typename T> struct TPoint1 {
|
|||
stream->writeElement<T>(x);
|
||||
}
|
||||
|
||||
/// Implicit conversion to Scalar
|
||||
operator Scalar() const { return x; }
|
||||
|
||||
/// Return a readable string representation of this point
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
|
|
|
@ -98,7 +98,7 @@ template <typename _PointType, typename _VectorType> struct TRay {
|
|||
/// Set the origin
|
||||
inline void setOrigin(const PointType &pos) { o = pos; }
|
||||
|
||||
/// Set the origin
|
||||
/// Set the time
|
||||
inline void setTime(Scalar tval) { time = tval; }
|
||||
|
||||
/// Set the direction and update the reciprocal
|
||||
|
|
|
@ -758,7 +758,7 @@ protected:
|
|||
*/
|
||||
class MTS_EXPORT_CORE LocalWorker : public Worker {
|
||||
public:
|
||||
LocalWorker(const std::string &name,
|
||||
LocalWorker(int coreID, const std::string &name,
|
||||
Thread::EThreadPriority priority = Thread::ENormalPriority);
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
|
|
@ -156,7 +156,7 @@ extern MTS_EXPORT_CORE Float integrateCubicInterp1DN(size_t idx,
|
|||
* \return
|
||||
* 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);
|
||||
|
||||
/**
|
||||
|
@ -182,8 +182,8 @@ extern MTS_EXPORT_CORE Float sampleCubicInterp1D(size_t idx, Float *values,
|
|||
* \return
|
||||
* The sampled position
|
||||
*/
|
||||
extern MTS_EXPORT_CORE Float sampleCubicInterp1DN(size_t idx, Float *nodes,
|
||||
Float *values, size_t size, Float sample, Float *fval = NULL);
|
||||
extern MTS_EXPORT_CORE Float sampleCubicInterp1DN(size_t idx, const Float *nodes,
|
||||
const Float *values, size_t size, Float sample, Float *fval = NULL);
|
||||
|
||||
/**
|
||||
* \brief Evaluate a cubic spline interpolant of a uniformly sampled 2D function
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <mitsuba/core/timer.h>
|
||||
#include <mitsuba/core/atomic.h>
|
||||
|
||||
//#define MTS_NO_STATISTICS 1
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <intrin.h>
|
||||
#endif
|
||||
|
@ -110,6 +112,7 @@ public:
|
|||
inline uint64_t operator++() {
|
||||
#if defined(MTS_NO_STATISTICS)
|
||||
// do nothing
|
||||
return 0;
|
||||
#elif defined(_MSC_VER) && defined(_WIN64)
|
||||
const int offset = Thread::getID() & NUM_COUNTERS_MASK;
|
||||
_InterlockedExchangeAdd64(reinterpret_cast<__int64 volatile *>(&m_value[offset].value), 1);
|
||||
|
|
|
@ -66,6 +66,19 @@ public:
|
|||
/// Return the thread priority
|
||||
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
|
||||
*
|
||||
|
|
|
@ -291,7 +291,18 @@ inline Float signum(Float value) {
|
|||
return -1;
|
||||
else if (value > 0)
|
||||
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
|
||||
|
|
|
@ -165,6 +165,9 @@ template <typename T> struct TVector1 {
|
|||
stream->writeElement<T>(x);
|
||||
}
|
||||
|
||||
/// Implicit conversion to Scalar
|
||||
operator Scalar() const { return x; }
|
||||
|
||||
/// Return a readable string representation of this vector
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
|
||||
Shader *createShader(Renderer *renderer) const;
|
||||
|
||||
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
@ -128,6 +130,8 @@ public:
|
|||
|
||||
Shader *createShader(Renderer *renderer) const;
|
||||
|
||||
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
@ -185,6 +189,8 @@ public:
|
|||
|
||||
Shader *createShader(Renderer *renderer) const;
|
||||
|
||||
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
@ -242,6 +248,8 @@ public:
|
|||
|
||||
Shader *createShader(Renderer *renderer) const;
|
||||
|
||||
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
@ -302,6 +310,8 @@ public:
|
|||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
ref<Bitmap> getBitmap(const Vector2i &resolutionHint) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
ref<const Texture> m_a, m_b;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#endif
|
||||
|
||||
#if defined(SINGLE_PRECISION)
|
||||
/// 32 byte temporary storage for intersection computations
|
||||
/// 64 byte temporary storage for intersection computations
|
||||
#define MTS_KD_INTERSECTION_TEMP 64
|
||||
#else
|
||||
#define MTS_KD_INTERSECTION_TEMP 128
|
||||
|
|
|
@ -74,8 +74,17 @@ public:
|
|||
/// Serialize to a binary data stream
|
||||
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()
|
||||
protected:
|
||||
|
@ -109,6 +118,18 @@ public:
|
|||
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
|
||||
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()
|
||||
protected:
|
||||
Texture2D(const Properties &props);
|
||||
|
|
|
@ -212,7 +212,10 @@ public:
|
|||
Float mean = 0, meanSqr = 0.0f;
|
||||
sampleCount = 0;
|
||||
|
||||
while (!stop) {
|
||||
while (true) {
|
||||
if (stop)
|
||||
return;
|
||||
|
||||
rRec.newQuery(RadianceQueryRecord::ESensorRay, sensor->getMedium());
|
||||
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 distSqr = (ray(diskDistance) - node.photon.getPosition()).lengthSquared();
|
||||
|
||||
if (distSqr < radSqr) {
|
||||
if (diskDistance > 0 && distSqr < radSqr) {
|
||||
Float weight = K2(distSqr/radSqr)/radSqr;
|
||||
|
||||
Vector wi = -node.photon.getDirection();
|
||||
|
|
|
@ -445,7 +445,7 @@ public:
|
|||
/* Exhaustively recurse into all specular lobes? */
|
||||
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 */
|
||||
int maxDepth = m_maxDepth == -1 ? INT_MAX : (m_maxDepth-rRec.depth);
|
||||
if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance && m_globalPhotonMap.get())
|
||||
|
|
|
@ -48,6 +48,9 @@ bool PathEdge::sampleNext(const Scene *scene, Sampler *sampler,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
if (!medium) {
|
||||
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
||||
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
||||
|
@ -103,6 +106,9 @@ bool PathEdge::perturbDirection(const Scene *scene,
|
|||
}
|
||||
d = ray.d;
|
||||
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
if (!medium) {
|
||||
weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
|
||||
pdf[ERadiance] = pdf[EImportance] = 1.0f;
|
||||
|
|
|
@ -251,8 +251,8 @@ extern "C" {
|
|||
* ========================== */
|
||||
|
||||
Bitmap::Bitmap(EPixelFormat pFormat, EComponentFormat cFormat,
|
||||
const Vector2i &size, int channelCount) : m_pixelFormat(pFormat),
|
||||
m_componentFormat(cFormat), m_size(size), m_channelCount(channelCount) {
|
||||
const Vector2i &size, uint8_t channelCount, uint8_t *data) : m_pixelFormat(pFormat),
|
||||
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");
|
||||
|
||||
if (m_componentFormat == EUInt8)
|
||||
|
@ -262,10 +262,13 @@ Bitmap::Bitmap(EPixelFormat pFormat, EComponentFormat cFormat,
|
|||
|
||||
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) {
|
||||
/* Try to automatically detect the file format */
|
||||
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 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
|
||||
return bytesPerRow * (size_t) m_size.y;
|
||||
}
|
||||
|
@ -392,7 +395,7 @@ int Bitmap::getBytesPerComponent() const {
|
|||
}
|
||||
|
||||
Bitmap::~Bitmap() {
|
||||
if (m_data)
|
||||
if (m_data && m_ownsData)
|
||||
freeAligned(m_data);
|
||||
}
|
||||
|
||||
|
@ -502,7 +505,7 @@ void Bitmap::accumulate(const Bitmap *bitmap, Point2i sourceOffset,
|
|||
return;
|
||||
|
||||
const size_t
|
||||
columns = size.x * m_channelCount,
|
||||
columns = (size_t) size.x * m_channelCount,
|
||||
pixelStride = getBytesPerPixel(),
|
||||
sourceStride = bitmap->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) {
|
||||
if (m_pixelFormat != ERGB && m_pixelFormat != ERGBA)
|
||||
Log(EError, "colorBalance(): expected a RGB or RGBA image!");
|
||||
|
@ -1132,9 +1379,8 @@ void Bitmap::applyMatrix(Float matrix_[3][3]) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// 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 bcv,
|
||||
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();
|
||||
|
||||
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()) {
|
||||
/* Re-sample along the X direction */
|
||||
Resampler<Scalar> r(rfilter, bch, source->getWidth(), target->getWidth());
|
||||
|
@ -1349,6 +1606,7 @@ void Bitmap::readPNG(Stream *stream) {
|
|||
|
||||
size_t bufferSize = getBufferSize();
|
||||
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
||||
m_ownsData = true;
|
||||
rows = new png_bytep[m_size.y];
|
||||
size_t rowBytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||
Assert(rowBytes == getBufferSize() / m_size.y);
|
||||
|
@ -1505,6 +1763,7 @@ void Bitmap::readJPEG(Stream *stream) {
|
|||
* (size_t) cinfo.output_components;
|
||||
|
||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||
m_ownsData = true;
|
||||
|
||||
boost::scoped_array<uint8_t*> scanlines(new uint8_t*[m_size.y]);
|
||||
for (int i=0; i<m_size.y; ++i)
|
||||
|
@ -1756,7 +2015,7 @@ void Bitmap::readOpenEXR(Stream *stream, const std::string &_prefix) {
|
|||
|
||||
updateChannelCount();
|
||||
m_gamma = 1.0f;
|
||||
Assert(m_channelCount == (int) sourceChannels.size());
|
||||
Assert(m_channelCount == (uint8_t) sourceChannels.size());
|
||||
|
||||
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 */
|
||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||
m_ownsData = true;
|
||||
char *ptr = (char *) m_data;
|
||||
|
||||
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;
|
||||
|
||||
/* Tell OpenEXR where the image data should be put */
|
||||
|
@ -2169,6 +2429,7 @@ void Bitmap::readTGA(Stream *stream) {
|
|||
rowSize = bufferSize / height;
|
||||
|
||||
m_data = static_cast<uint8_t *>(allocAligned(bufferSize));
|
||||
m_ownsData = true;
|
||||
int channels = bpp/8;
|
||||
|
||||
if (!rle) {
|
||||
|
@ -2259,6 +2520,7 @@ void Bitmap::readBMP(Stream *stream) {
|
|||
|
||||
size_t bufferSize = getBufferSize();
|
||||
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);
|
||||
|
||||
|
@ -2396,6 +2658,7 @@ void Bitmap::readRGBE(Stream *stream) {
|
|||
m_channelCount = 3;
|
||||
m_gamma = 1.0f;
|
||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||
m_ownsData = true;
|
||||
float *data = (float *) m_data;
|
||||
|
||||
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!");
|
||||
|
||||
m_data = static_cast<uint8_t *>(allocAligned(getBufferSize()));
|
||||
m_ownsData = true;
|
||||
float *data = (float *) m_data;
|
||||
|
||||
Stream::EByteOrder backup = stream->getByteOrder();
|
||||
|
@ -2626,7 +2890,7 @@ void Bitmap::writePFM(Stream *stream) const {
|
|||
float *dest = temp;
|
||||
|
||||
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++;
|
||||
source++;
|
||||
}
|
||||
|
|
|
@ -631,8 +631,9 @@ void Worker::start(Scheduler *scheduler, int workerIndex, int coreOffset) {
|
|||
Thread::start();
|
||||
}
|
||||
|
||||
LocalWorker::LocalWorker(const std::string &name,
|
||||
LocalWorker::LocalWorker(int coreID, const std::string &name,
|
||||
Thread::EThreadPriority priority) : Worker(name) {
|
||||
setCoreAffinity(coreID);
|
||||
m_coreCount = 1;
|
||||
#if !defined(__LINUX__)
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 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) {
|
||||
Float f0 = values[idx],
|
||||
f1 = values[idx+1],
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <mitsuba/core/lock.h>
|
||||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/core/atomic.h>
|
||||
#if defined(MTS_OPENMP)
|
||||
# include <omp.h>
|
||||
#endif
|
||||
|
@ -35,7 +36,6 @@
|
|||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
namespace {
|
||||
// Helper function to set a native thread name. MSDN:
|
||||
|
@ -69,6 +69,13 @@ void SetThreadName(const char* threadName, DWORD dwThreadID = -1) {
|
|||
} // namespace
|
||||
#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
|
||||
*/
|
||||
|
@ -80,13 +87,15 @@ struct Thread::ThreadPrivate {
|
|||
std::string name;
|
||||
bool running, joined;
|
||||
Thread::EThreadPriority priority;
|
||||
int coreAffinity;
|
||||
static ThreadLocal<Thread> *self;
|
||||
bool critical;
|
||||
boost::thread thread;
|
||||
|
||||
ThreadPrivate(const std::string & name_) :
|
||||
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() {
|
||||
#if defined(__WINDOWS__)
|
||||
return static_cast<int>(GetCurrentThreadId());
|
||||
#elif defined(__OSX__)
|
||||
return static_cast<int>(pthread_mach_thread_np(pthread_self()));
|
||||
#else
|
||||
return (int) pthread_self();
|
||||
return __thread_id;
|
||||
#elif defined(__OSX__) || defined(__LINUX__)
|
||||
/* pthread_self() doesn't provide nice increasing IDs, and syscall(SYS_gettid)
|
||||
causes a context switch. Thus, this function uses a thread-local variable
|
||||
to provide a nice linearly increasing sequence of thread IDs */
|
||||
return static_cast<int>(reinterpret_cast<intptr_t>(pthread_getspecific(__thread_id)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -297,9 +307,71 @@ bool Thread::setPriority(EThreadPriority priority) {
|
|||
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) {
|
||||
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);
|
||||
|
||||
if (thread->getPriority() != ENormalPriority)
|
||||
|
@ -319,6 +391,9 @@ void Thread::dispatch(Thread *thread) {
|
|||
#endif
|
||||
}
|
||||
|
||||
if (thread->getCoreAffinity() != -1)
|
||||
thread->setCoreAffinity(thread->getCoreAffinity());
|
||||
|
||||
try {
|
||||
thread->run();
|
||||
} catch (std::exception &e) {
|
||||
|
@ -410,12 +485,15 @@ int mts_omp_get_thread_num() {
|
|||
#endif
|
||||
|
||||
void Thread::staticInitialization() {
|
||||
#if defined(__OSX__)
|
||||
__mts_autorelease_init();
|
||||
#if defined(MTS_OPENMP)
|
||||
__omp_threadCount = omp_get_max_threads();
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__OSX__)
|
||||
__mts_autorelease_init();
|
||||
#if defined(MTS_OPENMP)
|
||||
__omp_threadCount = omp_get_max_threads();
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__LINUX__) || defined(__OSX__)
|
||||
pthread_key_create(&__thread_id, NULL);
|
||||
#endif
|
||||
detail::initializeGlobalTLS();
|
||||
detail::initializeLocalTLS();
|
||||
|
||||
|
@ -451,6 +529,9 @@ void Thread::staticShutdown() {
|
|||
delete ThreadPrivate::self;
|
||||
ThreadPrivate::self = NULL;
|
||||
detail::destroyGlobalTLS();
|
||||
#if defined(__LINUX__) || defined(__OSX__)
|
||||
pthread_key_delete(__thread_id);
|
||||
#endif
|
||||
#if defined(__OSX__)
|
||||
#if defined(MTS_OPENMP)
|
||||
if (__omp_key_created)
|
||||
|
@ -505,6 +586,13 @@ void Thread::initializeOpenMP(size_t threadCount) {
|
|||
SetThreadName(threadName.c_str());
|
||||
#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->joined = false;
|
||||
thread->d->fresolver = fResolver;
|
||||
|
|
|
@ -829,12 +829,11 @@ std::string memString(size_t size, bool precise) {
|
|||
|
||||
std::ostringstream os;
|
||||
os << std::setprecision(suffix == 0 ? 0 : (precise ? 4 : 1))
|
||||
<< std::fixed << value << suffixes[suffix];
|
||||
<< std::fixed << value << " " << suffixes[suffix];
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
||||
Float hypot2(Float a, Float b) {
|
||||
Float r;
|
||||
if (std::abs(a) > std::abs(b)) {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
ConstantSpectrumTexture::ConstantSpectrumTexture(Stream *stream, InstanceManager *manager)
|
||||
: Texture(stream, manager) {
|
||||
m_value = Spectrum(stream);
|
||||
|
@ -31,6 +32,12 @@ void ConstantSpectrumTexture::serialize(Stream *stream, InstanceManager *manager
|
|||
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)
|
||||
: Texture(stream, manager) {
|
||||
m_value = stream->readFloat();
|
||||
|
@ -41,6 +48,12 @@ void ConstantFloatTexture::serialize(Stream *stream, InstanceManager *manager) c
|
|||
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)
|
||||
: Texture(stream, manager) {
|
||||
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());
|
||||
}
|
||||
|
||||
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)
|
||||
: Texture(stream, manager) {
|
||||
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());
|
||||
}
|
||||
|
||||
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)
|
||||
: Texture(stream, manager) {
|
||||
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());
|
||||
}
|
||||
|
||||
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 {
|
||||
public:
|
||||
ConstantSpectrumTextureShader(Renderer *renderer, const Spectrum &value)
|
||||
|
|
|
@ -638,6 +638,8 @@ void export_core() {
|
|||
.def("getID", &Thread::getID)
|
||||
.def("setPriority", &Thread::setPriority)
|
||||
.def("getPriority", &Thread::getPriority)
|
||||
.def("setCoreAffinity", &Thread::setCoreAffinity)
|
||||
.def("getCoreAffinity", &Thread::getCoreAffinity)
|
||||
.def("setCritical", &Thread::setCritical)
|
||||
.def("getCritical", &Thread::getCritical)
|
||||
.def("setName", &Thread::setName)
|
||||
|
@ -871,8 +873,8 @@ void export_core() {
|
|||
.def("getCoreCount", &Worker::getCoreCount)
|
||||
.def("isRemoteWorker", &Worker::isRemoteWorker);
|
||||
|
||||
BP_CLASS(LocalWorker, Worker, bp::init<const std::string>())
|
||||
.def(bp::init<const std::string, Thread::EThreadPriority>());
|
||||
BP_CLASS(LocalWorker, Worker, (bp::init<int, const std::string>()))
|
||||
.def(bp::init<int, const std::string, Thread::EThreadPriority>());
|
||||
|
||||
BP_CLASS(RemoteWorker, Worker, (bp::init<const std::string, Stream *>()))
|
||||
.def("getNodeName", &RemoteWorker::getNodeName, BP_RETURN_VALUE);
|
||||
|
|
|
@ -351,12 +351,70 @@ void export_render() {
|
|||
.def("serialize", triMesh_serialize2)
|
||||
.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("setShutterOpen", &Sensor::setShutterOpen)
|
||||
.def("getShutterOpenTime", &Sensor::getShutterOpenTime)
|
||||
.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_setWorldTransform2)(AnimatedTransform *) = &ProjectiveCamera::setWorldTransform;
|
||||
const Transform (ProjectiveCamera::*projectiveCamera_getWorldTransform1)(Float t) const = &ProjectiveCamera::getWorldTransform;
|
||||
|
|
|
@ -333,7 +333,7 @@ void Scene::initialize() {
|
|||
}
|
||||
if (primitiveCount != effPrimitiveCount) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,10 @@ SceneHandler::SceneHandler(const SAXParser *parser,
|
|||
m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL);
|
||||
m_tags["spectrum"] = TagEntry(ESpectrum, (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["alias"] = TagEntry(EAlias, (Class *) NULL);
|
||||
|
||||
|
||||
XMLTransService::Codes failReason;
|
||||
m_transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(
|
||||
"UTF-8", failReason, TRANSCODE_BLOCKSIZE);
|
||||
|
|
|
@ -47,7 +47,7 @@ Spectrum Texture::getMinimum() const { NotImplementedError("getMinimum"); }
|
|||
Spectrum Texture::getMaximum() const { NotImplementedError("getMaximum"); }
|
||||
bool Texture::isConstant() const { NotImplementedError("isConstant"); }
|
||||
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() {
|
||||
return this;
|
||||
|
@ -100,6 +100,22 @@ Spectrum Texture2D::eval(const Intersection &its, bool filter) const {
|
|||
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(Texture2D, true, Texture)
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -276,7 +276,8 @@ public:
|
|||
Sampler *sampler) const {
|
||||
Float rand = sampler->next1D(), sampledDistance;
|
||||
Float samplingDensity = m_samplingDensity;
|
||||
if (rand <= m_mediumSamplingWeight) {
|
||||
|
||||
if (rand < m_mediumSamplingWeight) {
|
||||
rand /= m_mediumSamplingWeight;
|
||||
if (m_strategy != EMaximum) {
|
||||
/* Choose the sampling density to be used */
|
||||
|
|
|
@ -258,7 +258,7 @@ int mitsuba_app(int argc, char **argv) {
|
|||
/* Configure the scheduling subsystem */
|
||||
Scheduler *scheduler = Scheduler::getInstance();
|
||||
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, ";");
|
||||
|
||||
/* Establish network connections to nested servers */
|
||||
|
|
|
@ -219,7 +219,7 @@ int mtssrv(int argc, char **argv) {
|
|||
/* Configure the scheduling subsystem */
|
||||
Scheduler *scheduler = Scheduler::getInstance();
|
||||
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, ";");
|
||||
|
||||
/* Establish network connections to nested servers */
|
||||
|
|
|
@ -234,7 +234,7 @@ int mtsutil(int argc, char **argv) {
|
|||
/* Configure the scheduling subsystem */
|
||||
Scheduler *scheduler = Scheduler::getInstance();
|
||||
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, ";");
|
||||
|
||||
/* Establish network connections to nested servers */
|
||||
|
|
|
@ -266,7 +266,7 @@ void MainWindow::initWorkers() {
|
|||
m_workerPriority = (Thread::EThreadPriority)
|
||||
settings.value("workerPriority", (int) Thread::ELowPriority).toInt();
|
||||
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;
|
||||
QList<QVariant> connectionData = settings.value("connections").toList();
|
||||
|
@ -314,7 +314,7 @@ void MainWindow::initWorkers() {
|
|||
QMessageBox::warning(this, tr("Scheduler warning"),
|
||||
tr("There must be at least one worker thread -- forcing creation of one."),
|
||||
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();
|
||||
|
@ -1312,7 +1312,8 @@ void MainWindow::on_actionSettings_triggered() {
|
|||
ref<Scheduler> sched = Scheduler::getInstance();
|
||||
sched->pause();
|
||||
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);
|
||||
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) {
|
||||
int ox = vwu.offset.x, oy = vwu.offset.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;
|
||||
|
||||
/* Use desaturated colors to highlight the host
|
||||
|
@ -1753,17 +1757,22 @@ void MainWindow::drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit
|
|||
break;
|
||||
}
|
||||
|
||||
if (vwu.size.x < 3 || vwu.size.y < 3)
|
||||
return;
|
||||
float scale = .7f * (color[0] * 0.212671f + color[1] * 0.715160f + color[2] * 0.072169f);
|
||||
|
||||
drawHLine(context, ox, oy, ox + 3, color);
|
||||
drawHLine(context, ex - 4, oy, ex - 1, color);
|
||||
drawHLine(context, ox, ey - 1, ox + 3, color);
|
||||
drawHLine(context, ex - 4, ey - 1, ex - 1, color);
|
||||
drawVLine(context, ox, oy, oy + 3, color);
|
||||
drawVLine(context, ex - 1, oy, oy + 3, color);
|
||||
drawVLine(context, ex - 1, ey - 4, ey - 1, color);
|
||||
drawVLine(context, ox, ey - 4, ey - 1, color);
|
||||
float ncol[3] = {
|
||||
color[0]*scale,
|
||||
color[1]*scale,
|
||||
color[2]*scale
|
||||
};
|
||||
|
||||
drawHLine(context, ox, oy, ox + 3, ncol);
|
||||
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) {
|
||||
|
|
|
@ -385,7 +385,7 @@ void RenderSettingsDialog::apply(SceneContext *ctx) {
|
|||
filmProps.setInteger("width", size.x, 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("cropHeight", cropSize.y, false);
|
||||
filmProps.setInteger("cropOffsetX", cropOffset.x, false);
|
||||
|
|
|
@ -231,6 +231,16 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
|
|||
setProperties(ctx->doc, rfilter,
|
||||
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
|
||||
// ====================================================================
|
||||
|
|
|
@ -3,9 +3,10 @@ uniform float multiplier;
|
|||
|
||||
void main() {
|
||||
vec4 color = texture2D(source, gl_TexCoord[0].xy);
|
||||
float luminance = multiplier * (color.r * 0.212671 + color.g * 0.715160 + color.b * 0.072169);
|
||||
if (luminance < 0.0 || luminance != luminance || luminance == 1024.0)
|
||||
float luminance = color.r * 0.212671 + color.g * 0.715160 + color.b * 0.072169;
|
||||
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 = luminance * multiplier;
|
||||
float logLuminance = log(1e-3 + luminance);
|
||||
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(shapegroup shapegroup.h shapegroup.cpp)
|
||||
add_shape(instance instance.h instance.cpp)
|
||||
add_shape(heightfield heightfield.cpp)
|
||||
#add_shape(deformable deformable.cpp)
|
||||
add_shape(ply ply.cpp ply/ply_parser.cpp
|
||||
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('instance', ['instance.cpp'])
|
||||
plugins += env.SharedLibrary('cube', ['cube.cpp'])
|
||||
plugins += env.SharedLibrary('heightfield', ['heightfield.cpp'])
|
||||
#plugins += env.SharedLibrary('deformable', ['deformable.cpp'])
|
||||
|
||||
Export('plugins')
|
||||
|
|
|
@ -105,7 +105,7 @@ public:
|
|||
Vector dpdu = trafo(Vector(1, 0, 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!");
|
||||
|
||||
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_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!");
|
||||
}
|
||||
|
||||
|
|
|
@ -428,7 +428,7 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
ref<Bitmap> getBitmap() const {
|
||||
ref<Bitmap> getBitmap(const Vector2i &/* unused */) const {
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* \begin{xml}[caption=Scaling the contents of a bitmap texture]
|
||||
* <texture type="scale">
|
||||
* <float name="scale" value="0.5"/>
|
||||
|
@ -46,7 +47,6 @@ MTS_NAMESPACE_BEGIN
|
|||
* </texture>
|
||||
* </texture>
|
||||
* \end{xml}
|
||||
|
||||
*/
|
||||
|
||||
class ScalingTexture : public Texture {
|
||||
|
@ -102,6 +102,23 @@ public:
|
|||
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::ostringstream oss;
|
||||
oss << "ScalingTexture[" << endl
|
||||
|
|
Loading…
Reference in New Issue