merge
commit
b3bde09b8a
|
@ -42,7 +42,6 @@
|
|||
#include <mitsuba/core/constants.h>
|
||||
#include <mitsuba/core/fwd.h>
|
||||
#include <mitsuba/render/fwd.h>
|
||||
#include <mitsuba/core/math.h>
|
||||
#include <mitsuba/core/object.h>
|
||||
#include <mitsuba/core/ref.h>
|
||||
#include <mitsuba/core/tls.h>
|
||||
|
|
|
@ -33,7 +33,10 @@ MTS_NAMESPACE_BEGIN
|
|||
|
||||
/// Write a Log message to the console (to be used within subclasses of <tt>Object</tt>)
|
||||
#define Log(level, fmt, ...) do { \
|
||||
mitsuba::Logger *logger = mitsuba::Thread::getThread()->getLogger(); \
|
||||
mitsuba::Thread *thread = mitsuba::Thread::getThread(); \
|
||||
if (EXPECT_NOT_TAKEN(thread == NULL)) \
|
||||
throw std::runtime_error("Null thread pointer"); \
|
||||
mitsuba::Logger *logger = thread->getLogger(); \
|
||||
if (level >= logger->getLogLevel()) \
|
||||
logger->log(level, m_theClass, \
|
||||
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
|
@ -44,7 +47,10 @@ MTS_NAMESPACE_BEGIN
|
|||
* outside of classes that derive from Object)
|
||||
*/
|
||||
#define SLog(level, fmt, ...) do { \
|
||||
mitsuba::Logger *logger = mitsuba::Thread::getThread()->getLogger(); \
|
||||
mitsuba::Thread *thread = mitsuba::Thread::getThread(); \
|
||||
if (EXPECT_NOT_TAKEN(thread == NULL)) \
|
||||
throw std::runtime_error("Null thread pointer"); \
|
||||
mitsuba::Logger *logger = thread->getLogger(); \
|
||||
if (level >= logger->getLogLevel()) \
|
||||
logger->log(level, NULL, \
|
||||
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
|
|
|
@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \brief Generic LRU cache implementation
|
||||
*
|
||||
* Based on the bimap implementation by Tim Day
|
||||
* (http://www.bottlenose.demon.co.uk/article/lru.pdf).
|
||||
* (http://timday.bitbucket.org/lru.html).
|
||||
*
|
||||
* This cache does not support multithreading out of the box -- it
|
||||
* will need to be protected using some form of locking mechanism.
|
||||
|
|
|
@ -140,7 +140,7 @@ public:
|
|||
perm[i] = i;
|
||||
|
||||
/* Build the kd-tree and compute a suitable permutation of the elements */
|
||||
m_root = build(m_aabb, 0, &perm[0], &temp[0], &perm[0], &perm[m_items.size()]);
|
||||
m_root = build(m_aabb, 0, &perm[0], &temp[0], &perm[0], &perm[0] + m_items.size());
|
||||
|
||||
/* Apply the permutation */
|
||||
permute_inplace(&m_items[0], perm);
|
||||
|
|
|
@ -141,6 +141,7 @@ protected:
|
|||
}
|
||||
|
||||
inline static void destruct(void *data) {
|
||||
if (data)
|
||||
delete static_cast<ValueType *>(data);
|
||||
}
|
||||
protected:
|
||||
|
|
|
@ -189,6 +189,13 @@ template <typename T> inline TVector1<T> normalize(const TVector1<T> &v) {
|
|||
return v / v.length();
|
||||
}
|
||||
|
||||
template <typename T> inline TVector1<T> normalizeStrict(const TVector1<T> &v, const char *errMsg) {
|
||||
Float length = v.length();
|
||||
if (length == 0)
|
||||
SLog(EError, "normalizeStrict(): %s", errMsg);
|
||||
return v / length;
|
||||
}
|
||||
|
||||
template <> inline TVector1<int> TVector1<int>::operator/(int s) const {
|
||||
#ifdef MTS_DEBUG
|
||||
if (s == 0)
|
||||
|
@ -380,6 +387,13 @@ template <typename T> inline TVector2<T> normalize(const TVector2<T> &v) {
|
|||
return v / v.length();
|
||||
}
|
||||
|
||||
template <typename T> inline TVector2<T> normalizeStrict(const TVector2<T> &v, const char *errMsg) {
|
||||
Float length = v.length();
|
||||
if (length == 0)
|
||||
SLog(EError, "normalizeStrict(): %s", errMsg);
|
||||
return v / length;
|
||||
}
|
||||
|
||||
template <> inline TVector2<int> TVector2<int>::operator/(int s) const {
|
||||
#ifdef MTS_DEBUG
|
||||
if (s == 0)
|
||||
|
@ -582,6 +596,13 @@ template <typename T> inline TVector3<T> normalize(const TVector3<T> &v) {
|
|||
return v / v.length();
|
||||
}
|
||||
|
||||
template <typename T> inline TVector3<T> normalizeStrict(const TVector3<T> &v, const char *errMsg) {
|
||||
Float length = v.length();
|
||||
if (length == 0)
|
||||
SLog(EError, "normalizeStrict(): %s", errMsg);
|
||||
return v / length;
|
||||
}
|
||||
|
||||
template <> inline TVector3<int> TVector3<int>::operator/(int s) const {
|
||||
#ifdef MTS_DEBUG
|
||||
if (s == 0)
|
||||
|
@ -781,6 +802,13 @@ template <typename T> inline TVector4<T> normalize(const TVector4<T> &v) {
|
|||
return v / v.length();
|
||||
}
|
||||
|
||||
template <typename T> inline TVector4<T> normalizeStrict(const TVector4<T> &v, const char *errMsg) {
|
||||
Float length = v.length();
|
||||
if (length == 0)
|
||||
SLog(EError, "normalizeStrict(): %s", errMsg);
|
||||
return v / length;
|
||||
}
|
||||
|
||||
template <> inline TVector4<int> TVector4<int>::operator/(int s) const {
|
||||
#ifdef MTS_DEBUG
|
||||
if (s == 0)
|
||||
|
|
|
@ -101,6 +101,9 @@ public:
|
|||
/// Free the memory taken up by staticInitialization()
|
||||
static void staticShutdown();
|
||||
|
||||
/// Push a cleanup handler to be executed after loading the scene is done
|
||||
static void pushCleanupHandler(void (*cleanup)());
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Implementation of the SAX DocumentHandler interface
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
|
@ -310,6 +310,32 @@ protected:
|
|||
/// Load a Mitsuba compressed triangle mesh substream
|
||||
void loadCompressed(Stream *stream, int idx = 0);
|
||||
|
||||
/**
|
||||
* \brief Reads the header information of a compressed file, returning
|
||||
* the version ID.
|
||||
*
|
||||
* This function assumes the stream is at the beginning of the compressed
|
||||
* file and leaves the stream located right after the header.
|
||||
*/
|
||||
static short readHeader(Stream *stream);
|
||||
|
||||
/**
|
||||
* \brief Read the idx-th entry from the offset diccionary at the end of
|
||||
* the stream, which has to be open already, given the file version tag.
|
||||
* This function modifies the position of the stream.
|
||||
*/
|
||||
static size_t readOffset(Stream *stream, short version, int idx);
|
||||
|
||||
/**
|
||||
* \brief Read the entirety of the end-of-file offset dictionary from the
|
||||
* already open stream, replacing the contents of the input vector.
|
||||
* If the file is not large enough the function returns -1
|
||||
* and does not modify the vector.
|
||||
* This function modifies the position of the stream.
|
||||
*/
|
||||
static int readOffsetDictionary(Stream *stream, short version,
|
||||
std::vector<size_t>& outOffsets);
|
||||
|
||||
/// Prepare internal tables for sampling uniformly wrt. area
|
||||
void prepareSamplingTable();
|
||||
protected:
|
||||
|
|
|
@ -8,8 +8,8 @@ macro(add_film)
|
|||
endmacro()
|
||||
|
||||
add_film(mfilm mfilm.cpp)
|
||||
add_film(ldrfilm ldrfilm.cpp banner.h)
|
||||
add_film(hdrfilm hdrfilm.cpp banner.h)
|
||||
add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW)
|
||||
add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW)
|
||||
|
||||
if (OPENEXR_FOUND)
|
||||
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})
|
||||
|
|
|
@ -50,7 +50,7 @@ void PSSMLTSampler::serialize(Stream *stream, InstanceManager *manager) const {
|
|||
}
|
||||
|
||||
void PSSMLTSampler::configure() {
|
||||
m_logRatio = -std::log(m_s2/m_s1);
|
||||
m_logRatio = -math::fastlog(m_s2/m_s1);
|
||||
m_time = 0;
|
||||
m_largeStepTime = 0;
|
||||
m_largeStep = false;
|
||||
|
|
|
@ -238,7 +238,7 @@ extern "C" {
|
|||
p->mgr.free_in_buffer = 0;
|
||||
}
|
||||
|
||||
METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) {
|
||||
METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) throw(std::runtime_error) {
|
||||
char msg[JMSG_LENGTH_MAX];
|
||||
(*cinfo->err->format_message) (cinfo, msg);
|
||||
SLog(EError, "Critcal libjpeg error: %s", msg);
|
||||
|
@ -1370,7 +1370,7 @@ void Bitmap::writePNG(Stream *stream, int compression) const {
|
|||
text[i].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
}
|
||||
|
||||
png_set_text(png_ptr, info_ptr, text, keys.size());
|
||||
png_set_text(png_ptr, info_ptr, text, (int) keys.size());
|
||||
|
||||
if (m_gamma == -1)
|
||||
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
|
||||
|
|
|
@ -373,9 +373,9 @@ void Thread::yield() {
|
|||
void Thread::exit() {
|
||||
Log(EDebug, "Thread \"%s\" has finished", d->name.c_str());
|
||||
d->running = false;
|
||||
decRef();
|
||||
ThreadPrivate::self->set(NULL);
|
||||
Assert(ThreadPrivate::self->get() == this);
|
||||
detail::destroyLocalTLS();
|
||||
decRef();
|
||||
}
|
||||
|
||||
std::string Thread::toString() const {
|
||||
|
|
|
@ -17,16 +17,26 @@
|
|||
*/
|
||||
|
||||
#include <mitsuba/core/tls.h>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <set>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
|
||||
#if defined(__OSX__)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
||||
/* The native TLS classes on Linux/MacOS/Windows only support a limited number
|
||||
of dynamically allocated entries (usually 1024 or 1088). Furthermore, they
|
||||
do not provide appropriate cleanup semantics when the TLS object or one of
|
||||
|
@ -45,16 +55,37 @@ struct TLSEntry {
|
|||
inline TLSEntry() : data(NULL), destructFunctor(NULL) { }
|
||||
};
|
||||
|
||||
/// Per-thread TLS entry map
|
||||
struct PerThreadData {
|
||||
typedef boost::unordered_map<void *, TLSEntry> Map;
|
||||
/// boost multi-index element to act as replacement of map<Key,T>
|
||||
template<typename T1, typename T2>
|
||||
struct mutable_pair {
|
||||
mutable_pair(const T1 &f, const T2 &s) : first(f), second(s) { }
|
||||
|
||||
Map map;
|
||||
boost::mutex mutex;
|
||||
T1 first;
|
||||
mutable T2 second;
|
||||
};
|
||||
|
||||
/// List of all PerThreadData data structures (one for each thred)
|
||||
std::set<PerThreadData *> ptdGlobal;
|
||||
/// Per-thread TLS entry map
|
||||
struct PerThreadData {
|
||||
typedef mutable_pair<void *, TLSEntry> MapData;
|
||||
typedef mi::member<MapData, void *, &MapData::first> key_member;
|
||||
struct seq_tag {};
|
||||
struct key_tag {};
|
||||
|
||||
typedef mi::multi_index_container<MapData,
|
||||
mi::indexed_by<
|
||||
mi::hashed_unique<mi::tag<key_tag>, key_member>,
|
||||
mi::sequenced<mi::tag<seq_tag> >
|
||||
>
|
||||
> Map;
|
||||
typedef mi::index<Map, key_tag>::type::iterator key_iterator;
|
||||
typedef mi::index<Map, seq_tag>::type::reverse_iterator reverse_iterator;
|
||||
|
||||
Map map;
|
||||
boost::recursive_mutex mutex;
|
||||
};
|
||||
|
||||
/// List of all PerThreadData data structures (one for each thread)
|
||||
boost::unordered_set<PerThreadData *> ptdGlobal;
|
||||
/// Lock to protect ptdGlobal
|
||||
boost::mutex ptdGlobalLock;
|
||||
|
||||
|
@ -79,10 +110,10 @@ struct ThreadLocalBase::ThreadLocalPrivate {
|
|||
and clean up where necessary */
|
||||
boost::lock_guard<boost::mutex> guard(ptdGlobalLock);
|
||||
|
||||
for (std::set<PerThreadData *>::iterator it = ptdGlobal.begin();
|
||||
for (boost::unordered_set<PerThreadData *>::iterator it = ptdGlobal.begin();
|
||||
it != ptdGlobal.end(); ++it) {
|
||||
PerThreadData *ptd = *it;
|
||||
boost::unique_lock<boost::mutex> lock(ptd->mutex);
|
||||
boost::unique_lock<boost::recursive_mutex> lock(ptd->mutex);
|
||||
|
||||
PerThreadData::Map::iterator it2 = ptd->map.find(this);
|
||||
TLSEntry entry;
|
||||
|
@ -102,25 +133,32 @@ struct ThreadLocalBase::ThreadLocalPrivate {
|
|||
/// Look up a TLS entry. The goal is to make this operation very fast!
|
||||
std::pair<void *, bool> get() {
|
||||
bool existed = true;
|
||||
void *data;
|
||||
|
||||
#if defined(__OSX__)
|
||||
PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptdLocal);
|
||||
#else
|
||||
PerThreadData *ptd = ptdLocal;
|
||||
#endif
|
||||
if (EXPECT_NOT_TAKEN(!ptd))
|
||||
throw std::runtime_error("Internal error: call to ThreadLocalPrivate::get() "
|
||||
" precedes the construction of thread-specific data structures!");
|
||||
|
||||
/* This is an uncontended thread-local lock (i.e. not to worry) */
|
||||
boost::lock_guard<boost::mutex> guard(ptd->mutex);
|
||||
TLSEntry &entry = ptd->map[this];
|
||||
|
||||
if (EXPECT_NOT_TAKEN(!entry.data)) {
|
||||
boost::lock_guard<boost::recursive_mutex> guard(ptd->mutex);
|
||||
PerThreadData::key_iterator it = ptd->map.find(this);
|
||||
if (EXPECT_TAKEN(it != ptd->map.end())) {
|
||||
data = it->second.data;
|
||||
} else {
|
||||
/* This is the first access from this thread */
|
||||
entry.data = constructFunctor();
|
||||
TLSEntry entry;
|
||||
entry.data = data = constructFunctor();
|
||||
entry.destructFunctor = destructFunctor;
|
||||
ptd->map.insert(PerThreadData::MapData(this, entry));
|
||||
existed = false;
|
||||
}
|
||||
|
||||
return std::make_pair(entry.data, existed);
|
||||
return std::make_pair(data, existed);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -191,10 +229,11 @@ void destroyLocalTLS() {
|
|||
PerThreadData *ptd = ptdLocal;
|
||||
#endif
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(ptd->mutex);
|
||||
boost::unique_lock<boost::recursive_mutex> lock(ptd->mutex);
|
||||
|
||||
for (PerThreadData::Map::iterator it = ptd->map.begin();
|
||||
it != ptd->map.end(); ++it) {
|
||||
// Destroy the data in reverse order of creation
|
||||
for (PerThreadData::reverse_iterator it = mi::get<PerThreadData::seq_tag>(ptd->map).rbegin();
|
||||
it != mi::get<PerThreadData::seq_tag>(ptd->map).rend(); ++it) {
|
||||
TLSEntry &entry = it->second;
|
||||
entry.destructFunctor(entry.data);
|
||||
}
|
||||
|
|
|
@ -189,8 +189,8 @@ Transform Transform::glOrthographic(Float clipLeft, Float clipRight,
|
|||
}
|
||||
|
||||
Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) {
|
||||
Vector dir = normalize(t-p);
|
||||
Vector left = normalize(cross(up, dir));
|
||||
Vector dir = normalizeStrict(t-p, "lookAt(): 'origin' and 'target' coincide!");
|
||||
Vector left = normalizeStrict(cross(up, dir), "lookAt(): the forward and upward direction must be linearly independent!");
|
||||
Vector newUp = cross(dir, left);
|
||||
|
||||
Matrix4x4 result, inverse;
|
||||
|
|
|
@ -137,7 +137,7 @@ Point2 Warp::squareToStdNormal(const Point2 &sample) {
|
|||
}
|
||||
|
||||
Float Warp::squareToStdNormalPdf(const Point2 &pos) {
|
||||
return INV_TWOPI * std::exp(-(pos.x*pos.x + pos.y*pos.y)/2.0f);
|
||||
return INV_TWOPI * math::fastexp(-(pos.x*pos.x + pos.y*pos.y)/2.0f);
|
||||
}
|
||||
|
||||
static Float intervalToTent(Float sample) {
|
||||
|
|
|
@ -99,8 +99,8 @@ void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const {
|
|||
);
|
||||
|
||||
Point2i sourceOffset(
|
||||
glyph.tx.x * m_bitmap->getWidth(),
|
||||
glyph.tx.y * m_bitmap->getHeight());
|
||||
(int) (glyph.tx.x * m_bitmap->getWidth()),
|
||||
(int) (glyph.tx.y * m_bitmap->getHeight()));
|
||||
|
||||
dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size);
|
||||
|
||||
|
@ -120,7 +120,7 @@ Vector2i Font::getSize(const std::string &text) const {
|
|||
if (character == '\r')
|
||||
continue;
|
||||
if (character == '\n') {
|
||||
size.y += getMaxVerticalBearing()*(4.0 / 3.0);
|
||||
size.y += (int) (getMaxVerticalBearing()*(4.0 / 3.0));
|
||||
size.x = std::max(size.x, pos);
|
||||
pos = 0;
|
||||
continue;
|
||||
|
|
|
@ -206,7 +206,7 @@ struct path_to_python_str {
|
|||
|
||||
|
||||
struct TSpectrum_to_Spectrum {
|
||||
static PyObject* convert(const TSpectrum<Float, 3> &spectrum) {
|
||||
static PyObject* convert(const TSpectrum<Float, SPECTRUM_SAMPLES> &spectrum) {
|
||||
return bp::incref(bp::object(Spectrum(spectrum)).ptr());
|
||||
}
|
||||
};
|
||||
|
@ -532,7 +532,7 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4)
|
|||
|
||||
void export_core() {
|
||||
bp::to_python_converter<fs::path, path_to_python_str>();
|
||||
bp::to_python_converter<TSpectrum<Float, 3>, TSpectrum_to_Spectrum>();
|
||||
bp::to_python_converter<TSpectrum<Float, SPECTRUM_SAMPLES>, TSpectrum_to_Spectrum>();
|
||||
bp::implicitly_convertible<std::string, fs::path>();
|
||||
|
||||
bp::object coreModule(
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/render/scene.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
XERCES_CPP_NAMESPACE_USE
|
||||
|
@ -46,6 +47,10 @@ XERCES_CPP_NAMESPACE_USE
|
|||
level, NULL, __FILE__, __LINE__, fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
typedef void (*CleanupFun) ();
|
||||
typedef boost::unordered_set<CleanupFun> CleanupSet;
|
||||
static PrimitiveThreadLocal<CleanupSet> __cleanup_tls;
|
||||
|
||||
SceneHandler::SceneHandler(const SAXParser *parser,
|
||||
const ParameterMap ¶ms, NamedObjectMap *namedObjects,
|
||||
bool isIncludedFile) : m_parser(parser), m_params(params),
|
||||
|
@ -155,6 +160,13 @@ void SceneHandler::startDocument() {
|
|||
|
||||
void SceneHandler::endDocument() {
|
||||
SAssert(m_scene != NULL);
|
||||
|
||||
/* Call cleanup handlers */
|
||||
CleanupSet &cleanup = __cleanup_tls.get();
|
||||
for (CleanupSet::iterator it = cleanup.begin();
|
||||
it != cleanup.end(); ++it)
|
||||
(*it)();
|
||||
cleanup.clear();
|
||||
}
|
||||
|
||||
void SceneHandler::characters(const XMLCh* const name,
|
||||
|
@ -247,6 +259,10 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
|
|||
m_context.push(context);
|
||||
}
|
||||
|
||||
void SceneHandler::pushCleanupHandler(void (*cleanup)()) {
|
||||
__cleanup_tls.get().insert(cleanup);
|
||||
}
|
||||
|
||||
void SceneHandler::endElement(const XMLCh* const xmlName) {
|
||||
std::string name = transcode(xmlName);
|
||||
ParseContext &context = m_context.top();
|
||||
|
|
|
@ -171,7 +171,6 @@ static void readHelper(Stream *stream, bool fileDoublePrecision,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void TriMesh::loadCompressed(Stream *_stream, int index) {
|
||||
ref<Stream> stream = _stream;
|
||||
|
||||
|
@ -179,43 +178,12 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
|
|||
Log(EError, "Tried to unserialize a shape from a stream, "
|
||||
"which was not previously set to little endian byte order!");
|
||||
|
||||
short format = stream->readShort();
|
||||
if (format == 0x1C04)
|
||||
Log(EError, "Encountered a geometry file generated by an old "
|
||||
"version of Mitsuba. Please re-import the scene to update this file "
|
||||
"to the current format.");
|
||||
|
||||
if (format != MTS_FILEFORMAT_HEADER)
|
||||
Log(EError, "Encountered an invalid file format!");
|
||||
|
||||
short version = stream->readShort();
|
||||
if (version != MTS_FILEFORMAT_VERSION_V3 &&
|
||||
version != MTS_FILEFORMAT_VERSION_V4)
|
||||
Log(EError, "Encountered an incompatible file version!");
|
||||
const short version = readHeader(stream);
|
||||
|
||||
if (index != 0) {
|
||||
size_t streamSize = stream->getSize();
|
||||
|
||||
/* Determine the position of the requested substream. This
|
||||
is stored at the end of the file */
|
||||
stream->seek(streamSize - sizeof(uint32_t));
|
||||
uint32_t count = stream->readUInt();
|
||||
if (index < 0 || index > (int) count) {
|
||||
Log(EError, "Unable to unserialize mesh, "
|
||||
"shape index is out of range! (requested %i out of 0..%i)",
|
||||
index, count-1);
|
||||
}
|
||||
|
||||
// Seek to the correct position
|
||||
if (version == MTS_FILEFORMAT_VERSION_V4) {
|
||||
stream->seek(stream->getSize() - sizeof(uint64_t) * (count-index) - sizeof(uint32_t));
|
||||
stream->seek(stream->readSize());
|
||||
} else {
|
||||
stream->seek(stream->getSize() - sizeof(uint32_t) * (count-index + 1));
|
||||
stream->seek(stream->readUInt());
|
||||
}
|
||||
|
||||
stream->skip(sizeof(short) * 2);
|
||||
const size_t offset = readOffset(stream, version, index);
|
||||
stream->seek(offset);
|
||||
stream->skip(sizeof(short) * 2); // Skip the header
|
||||
}
|
||||
|
||||
stream = new ZStream(stream);
|
||||
|
@ -282,6 +250,91 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
|
|||
m_flipNormals = false;
|
||||
}
|
||||
|
||||
short TriMesh::readHeader(Stream *stream) {
|
||||
short format = stream->readShort();
|
||||
if (format == 0x1C04) {
|
||||
Log(EError, "Encountered a geometry file generated by an old "
|
||||
"version of Mitsuba. Please re-import the scene to update this file "
|
||||
"to the current format.");
|
||||
}
|
||||
if (format != MTS_FILEFORMAT_HEADER) {
|
||||
Log(EError, "Encountered an invalid file format!");
|
||||
}
|
||||
short version = stream->readShort();
|
||||
if (version != MTS_FILEFORMAT_VERSION_V3 &&
|
||||
version != MTS_FILEFORMAT_VERSION_V4) {
|
||||
Log(EError, "Encountered an incompatible file version!");
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
size_t TriMesh::readOffset(Stream *stream, short version, int idx) {
|
||||
const size_t streamSize = stream->getSize();
|
||||
|
||||
/* Determine the position of the requested substream. This is stored
|
||||
at the end of the file */
|
||||
stream->seek(streamSize - sizeof(uint32_t));
|
||||
uint32_t count = stream->readUInt();
|
||||
if (idx < 0 || idx > (int) count) {
|
||||
Log(EError, "Unable to unserialize mesh, "
|
||||
"shape index is out of range! (requested %i out of 0..%i)",
|
||||
idx, count-1);
|
||||
}
|
||||
|
||||
// Seek to the correct position
|
||||
if (version == MTS_FILEFORMAT_VERSION_V4) {
|
||||
stream->seek(stream->getSize() - sizeof(uint64_t) * (count-idx) - sizeof(uint32_t));
|
||||
return stream->readSize();
|
||||
} else {
|
||||
Assert(version == MTS_FILEFORMAT_VERSION_V3);
|
||||
stream->seek(stream->getSize() - sizeof(uint32_t) * (count-idx + 1));
|
||||
return stream->readUInt();
|
||||
}
|
||||
}
|
||||
|
||||
int TriMesh::readOffsetDictionary(Stream *stream, short version,
|
||||
std::vector<size_t>& outOffsets) {
|
||||
const size_t streamSize = stream->getSize();
|
||||
stream->seek(streamSize - sizeof(uint32_t));
|
||||
const uint32_t count = stream->readUInt();
|
||||
|
||||
// Check if the stream is large enough to contain that number of meshes
|
||||
const size_t minSize = sizeof(uint32_t) + count *
|
||||
( 2*sizeof(uint16_t) // Header
|
||||
+ sizeof(uint32_t) // Flags
|
||||
+ sizeof(char) // Name
|
||||
+ 2*sizeof(uint64_t) // Number of vertices and triangles
|
||||
+ 3*sizeof(float) // One vertex
|
||||
+ 3*sizeof(uint32_t)); // One triangle
|
||||
|
||||
if (streamSize >= minSize) {
|
||||
outOffsets.resize(count);
|
||||
if (version == MTS_FILEFORMAT_VERSION_V4) {
|
||||
stream->seek(stream->getSize() - sizeof(uint64_t) * count - sizeof(uint32_t));
|
||||
if (typeid(size_t) == typeid(uint64_t)) {
|
||||
stream->readArray(&outOffsets[0], count);
|
||||
} else {
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
outOffsets[i] = stream->readSize();
|
||||
}
|
||||
} else {
|
||||
stream->seek(stream->getSize() - sizeof(uint32_t) * (count + 1));
|
||||
Assert(version == MTS_FILEFORMAT_VERSION_V3);
|
||||
if (typeid(size_t) == typeid(uint32_t)) {
|
||||
stream->readArray(&outOffsets[0], count);
|
||||
} else {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
outOffsets[i] = (size_t) stream->readUInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
Log(EDebug, "The serialized mesh does not contain a valid dictionary");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
TriMesh::~TriMesh() {
|
||||
if (m_positions)
|
||||
delete[] m_positions;
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#if !defined(__MAXEXP_H)
|
||||
#define __MAXEXP_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <functional>
|
||||
#endif
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
class MaxExpDist {
|
||||
|
|
|
@ -52,8 +52,8 @@ public:
|
|||
Float eval(Float x) const {
|
||||
Float alpha = -1.0f / (2.0f * m_stddev*m_stddev);
|
||||
return std::max((Float) 0.0f,
|
||||
std::exp(alpha * x * x) -
|
||||
std::exp(alpha * m_radius * m_radius));
|
||||
math::fastexp(alpha * x * x) -
|
||||
math::fastexp(alpha * m_radius * m_radius));
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
|
|
|
@ -17,10 +17,17 @@
|
|||
*/
|
||||
|
||||
#include <mitsuba/render/trimesh.h>
|
||||
#include <mitsuba/render/scenehandler.h>
|
||||
#include <mitsuba/core/properties.h>
|
||||
#include <mitsuba/core/fstream.h>
|
||||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/core/timer.h>
|
||||
#include <mitsuba/core/lrucache.h>
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
/// How many files to keep open in the cache, per thread
|
||||
#define MTS_SERIALIZED_CACHE_SIZE 4
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -152,9 +159,8 @@ public:
|
|||
|
||||
/* Load the geometry */
|
||||
Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.filename().string().c_str());
|
||||
ref<FileStream> stream = new FileStream(filePath, FileStream::EReadOnly);
|
||||
ref<Timer> timer = new Timer();
|
||||
loadCompressed(stream, shapeIndex);
|
||||
loadCompressed(filePath, shapeIndex);
|
||||
Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)",
|
||||
m_triangleCount, m_vertexCount, timer->getMilliseconds());
|
||||
|
||||
|
@ -202,8 +208,92 @@ public:
|
|||
: TriMesh(stream, manager) { }
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Helper class for loading serialized meshes from the same file
|
||||
* repeatedly: it is common for scene to load multiple meshes from the same
|
||||
* file, most times even in ascending order. This class loads the mesh
|
||||
* offsets dictionary only once and keeps the stream open.
|
||||
*
|
||||
* Instances of this class are not thread safe.
|
||||
*/
|
||||
class MeshLoader {
|
||||
public:
|
||||
MeshLoader(const fs::path& filePath) {
|
||||
m_fstream = new FileStream(filePath, FileStream::EReadOnly);
|
||||
m_fstream->setByteOrder(Stream::ELittleEndian);
|
||||
const short version = SerializedMesh::readHeader(m_fstream);
|
||||
if (SerializedMesh::readOffsetDictionary(m_fstream,
|
||||
version, m_offsets) < 0) {
|
||||
// Assume there is a single mesh in the file at offset 0
|
||||
m_offsets.resize(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions the stream at the location for the given shape index.
|
||||
* Returns the modified stream.
|
||||
*/
|
||||
inline FileStream* seekStream(size_t shapeIndex) {
|
||||
if (shapeIndex > m_offsets.size()) {
|
||||
SLog(EError, "Unable to unserialize mesh, "
|
||||
"shape index is out of range! (requested %i out of 0..%i)",
|
||||
shapeIndex, (int) (m_offsets.size()-1));
|
||||
}
|
||||
const size_t pos = m_offsets[shapeIndex];
|
||||
m_fstream->seek(pos);
|
||||
return m_fstream;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<size_t> m_offsets;
|
||||
ref<FileStream> m_fstream;
|
||||
};
|
||||
|
||||
struct FileStreamCache : LRUCache<fs::path, std::less<fs::path>,
|
||||
boost::shared_ptr<MeshLoader> > {
|
||||
|
||||
inline boost::shared_ptr<MeshLoader> get(const fs::path& path) {
|
||||
bool dummy;
|
||||
return LRUCache::get(path, dummy);
|
||||
}
|
||||
|
||||
FileStreamCache() : LRUCache(MTS_SERIALIZED_CACHE_SIZE,
|
||||
&boost::make_shared<MeshLoader, const fs::path&>) {}
|
||||
};
|
||||
|
||||
/// Release all currently held offset caches / file streams
|
||||
static void flushCache() {
|
||||
m_cache.set(NULL);
|
||||
}
|
||||
|
||||
/// Loads the mesh from the thread-local file stream cache
|
||||
void loadCompressed(const fs::path& filePath, const int idx) {
|
||||
if (EXPECT_NOT_TAKEN(idx < 0)) {
|
||||
Log(EError, "Unable to unserialize mesh, "
|
||||
"shape index is negative! (requested %i out of 0..%i)", idx);
|
||||
}
|
||||
|
||||
// Get the thread local cache; create it if this is the first time
|
||||
FileStreamCache* cache = m_cache.get();
|
||||
if (EXPECT_NOT_TAKEN(cache == NULL)) {
|
||||
cache = new FileStreamCache();
|
||||
m_cache.set(cache);
|
||||
SceneHandler::pushCleanupHandler(&SerializedMesh::flushCache);
|
||||
}
|
||||
|
||||
boost::shared_ptr<MeshLoader> meshLoader = cache->get(filePath);
|
||||
Assert(meshLoader != NULL);
|
||||
TriMesh::loadCompressed(meshLoader->seekStream((size_t) idx));
|
||||
}
|
||||
|
||||
static ThreadLocal<FileStreamCache> m_cache;
|
||||
};
|
||||
|
||||
ThreadLocal<SerializedMesh::FileStreamCache> SerializedMesh::m_cache;
|
||||
|
||||
MTS_IMPLEMENT_CLASS_S(SerializedMesh, false, TriMesh)
|
||||
MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader");
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -218,7 +218,7 @@ void blueNoisePointSet(const Scene *scene, const std::vector<Shape *> &shapes,
|
|||
Cell &cell = cells[cellID];
|
||||
int arrayIndex = cell.firstIndex + trial;
|
||||
|
||||
if (arrayIndex > (int) samples.size() ||
|
||||
if (arrayIndex >= (int) samples.size() ||
|
||||
samples[arrayIndex].cellID != cellID ||
|
||||
cell.sample != -1)
|
||||
continue;
|
||||
|
|
Loading…
Reference in New Issue