Merge with default

metadata
Edgar Velazquez-Armendariz 2013-10-19 13:47:18 -04:00
commit ec3ade8ad2
51 changed files with 1492 additions and 93 deletions

View File

@ -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)

View File

@ -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':

View File

@ -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':

View File

@ -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':

View File

@ -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'

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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);

View File

@ -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
*

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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())

View File

@ -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;

View File

@ -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++;
}

View File

@ -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

View File

@ -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],

View File

@ -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;

View File

@ -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)) {

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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) {

View File

@ -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);

View File

@ -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
// ====================================================================

View File

@ -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);
}

View File

@ -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

View File

@ -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')

View File

@ -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)

677
src/shapes/heightfield.cpp Normal file
View File

@ -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

View File

@ -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!");
}

View File

@ -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();
}

View File

@ -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