merge with Edgar's latest changes
commit
82255028d6
|
@ -42,7 +42,6 @@
|
||||||
#include <mitsuba/core/constants.h>
|
#include <mitsuba/core/constants.h>
|
||||||
#include <mitsuba/core/fwd.h>
|
#include <mitsuba/core/fwd.h>
|
||||||
#include <mitsuba/render/fwd.h>
|
#include <mitsuba/render/fwd.h>
|
||||||
#include <mitsuba/core/math.h>
|
|
||||||
#include <mitsuba/core/object.h>
|
#include <mitsuba/core/object.h>
|
||||||
#include <mitsuba/core/ref.h>
|
#include <mitsuba/core/ref.h>
|
||||||
#include <mitsuba/core/tls.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>)
|
/// Write a Log message to the console (to be used within subclasses of <tt>Object</tt>)
|
||||||
#define Log(level, fmt, ...) do { \
|
#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()) \
|
if (level >= logger->getLogLevel()) \
|
||||||
logger->log(level, m_theClass, \
|
logger->log(level, m_theClass, \
|
||||||
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||||
|
@ -44,7 +47,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* outside of classes that derive from Object)
|
* outside of classes that derive from Object)
|
||||||
*/
|
*/
|
||||||
#define SLog(level, fmt, ...) do { \
|
#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()) \
|
if (level >= logger->getLogLevel()) \
|
||||||
logger->log(level, NULL, \
|
logger->log(level, NULL, \
|
||||||
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||||
|
|
|
@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \brief Generic LRU cache implementation
|
* \brief Generic LRU cache implementation
|
||||||
*
|
*
|
||||||
* Based on the bimap implementation by Tim Day
|
* 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
|
* This cache does not support multithreading out of the box -- it
|
||||||
* will need to be protected using some form of locking mechanism.
|
* will need to be protected using some form of locking mechanism.
|
||||||
|
|
|
@ -140,7 +140,7 @@ public:
|
||||||
perm[i] = i;
|
perm[i] = i;
|
||||||
|
|
||||||
/* Build the kd-tree and compute a suitable permutation of the elements */
|
/* 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 */
|
/* Apply the permutation */
|
||||||
permute_inplace(&m_items[0], perm);
|
permute_inplace(&m_items[0], perm);
|
||||||
|
|
|
@ -310,6 +310,31 @@ protected:
|
||||||
/// Load a Mitsuba compressed triangle mesh substream
|
/// Load a Mitsuba compressed triangle mesh substream
|
||||||
void loadCompressed(Stream *stream, int idx = 0);
|
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
|
/// Prepare internal tables for sampling uniformly wrt. area
|
||||||
void prepareSamplingTable();
|
void prepareSamplingTable();
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -8,8 +8,8 @@ macro(add_film)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
add_film(mfilm mfilm.cpp)
|
add_film(mfilm mfilm.cpp)
|
||||||
add_film(ldrfilm ldrfilm.cpp banner.h)
|
add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW)
|
||||||
add_film(hdrfilm hdrfilm.cpp banner.h)
|
add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW)
|
||||||
|
|
||||||
if (OPENEXR_FOUND)
|
if (OPENEXR_FOUND)
|
||||||
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})
|
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})
|
||||||
|
|
|
@ -238,7 +238,7 @@ extern "C" {
|
||||||
p->mgr.free_in_buffer = 0;
|
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];
|
char msg[JMSG_LENGTH_MAX];
|
||||||
(*cinfo->err->format_message) (cinfo, msg);
|
(*cinfo->err->format_message) (cinfo, msg);
|
||||||
SLog(EError, "Critcal libjpeg error: %s", 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;
|
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)
|
if (m_gamma == -1)
|
||||||
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
|
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
|
||||||
|
|
|
@ -373,9 +373,9 @@ void Thread::yield() {
|
||||||
void Thread::exit() {
|
void Thread::exit() {
|
||||||
Log(EDebug, "Thread \"%s\" has finished", d->name.c_str());
|
Log(EDebug, "Thread \"%s\" has finished", d->name.c_str());
|
||||||
d->running = false;
|
d->running = false;
|
||||||
decRef();
|
Assert(ThreadPrivate::self->get() == this);
|
||||||
ThreadPrivate::self->set(NULL);
|
|
||||||
detail::destroyLocalTLS();
|
detail::destroyLocalTLS();
|
||||||
|
decRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Thread::toString() const {
|
std::string Thread::toString() const {
|
||||||
|
|
|
@ -17,16 +17,26 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <mitsuba/core/tls.h>
|
#include <mitsuba/core/tls.h>
|
||||||
|
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/recursive_mutex.hpp>
|
||||||
#include <boost/thread/locks.hpp>
|
#include <boost/thread/locks.hpp>
|
||||||
#include <boost/unordered_map.hpp>
|
#include <boost/unordered_set.hpp>
|
||||||
#include <set>
|
|
||||||
|
#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__)
|
#if defined(__OSX__)
|
||||||
# include <pthread.h>
|
# include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace mi = boost::multi_index;
|
||||||
|
|
||||||
/* The native TLS classes on Linux/MacOS/Windows only support a limited number
|
/* The native TLS classes on Linux/MacOS/Windows only support a limited number
|
||||||
of dynamically allocated entries (usually 1024 or 1088). Furthermore, they
|
of dynamically allocated entries (usually 1024 or 1088). Furthermore, they
|
||||||
do not provide appropriate cleanup semantics when the TLS object or one of
|
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) { }
|
inline TLSEntry() : data(NULL), destructFunctor(NULL) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Per-thread TLS entry map
|
/// boost multi-index element to act as replacement of map<Key,T>
|
||||||
struct PerThreadData {
|
template<typename T1, typename T2>
|
||||||
typedef boost::unordered_map<void *, TLSEntry> Map;
|
struct mutable_pair {
|
||||||
|
mutable_pair(const T1 &f, const T2 &s) : first(f), second(s) { }
|
||||||
|
|
||||||
Map map;
|
T1 first;
|
||||||
boost::mutex mutex;
|
mutable T2 second;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// List of all PerThreadData data structures (one for each thred)
|
/// Per-thread TLS entry map
|
||||||
std::set<PerThreadData *> ptdGlobal;
|
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
|
/// Lock to protect ptdGlobal
|
||||||
boost::mutex ptdGlobalLock;
|
boost::mutex ptdGlobalLock;
|
||||||
|
|
||||||
|
@ -79,10 +110,10 @@ struct ThreadLocalBase::ThreadLocalPrivate {
|
||||||
and clean up where necessary */
|
and clean up where necessary */
|
||||||
boost::lock_guard<boost::mutex> guard(ptdGlobalLock);
|
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) {
|
it != ptdGlobal.end(); ++it) {
|
||||||
PerThreadData *ptd = *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);
|
PerThreadData::Map::iterator it2 = ptd->map.find(this);
|
||||||
TLSEntry entry;
|
TLSEntry entry;
|
||||||
|
@ -108,19 +139,24 @@ struct ThreadLocalBase::ThreadLocalPrivate {
|
||||||
#else
|
#else
|
||||||
PerThreadData *ptd = ptdLocal;
|
PerThreadData *ptd = ptdLocal;
|
||||||
#endif
|
#endif
|
||||||
|
if(EXPECT_NOT_TAKEN(!ptd)) {
|
||||||
|
throw std::runtime_error("null per-thread data");
|
||||||
|
}
|
||||||
|
|
||||||
/* This is an uncontended thread-local lock (i.e. not to worry) */
|
void *data;
|
||||||
boost::lock_guard<boost::mutex> guard(ptd->mutex);
|
boost::lock_guard<boost::recursive_mutex> guard(ptd->mutex);
|
||||||
TLSEntry &entry = ptd->map[this];
|
PerThreadData::key_iterator it = ptd->map.find(this);
|
||||||
|
if (EXPECT_TAKEN(it != ptd->map.end())) {
|
||||||
if (EXPECT_NOT_TAKEN(!entry.data)) {
|
data = it->second.data;
|
||||||
/* This is the first access from this thread */
|
} else {
|
||||||
entry.data = constructFunctor();
|
TLSEntry entry;
|
||||||
|
entry.data = data = constructFunctor();
|
||||||
entry.destructFunctor = destructFunctor;
|
entry.destructFunctor = destructFunctor;
|
||||||
|
ptd->map.insert(PerThreadData::MapData(this, entry));
|
||||||
existed = false;
|
existed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair(entry.data, existed);
|
return std::make_pair(data, existed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,10 +227,11 @@ void destroyLocalTLS() {
|
||||||
PerThreadData *ptd = ptdLocal;
|
PerThreadData *ptd = ptdLocal;
|
||||||
#endif
|
#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();
|
// Destroy the data in reverse order of creation
|
||||||
it != ptd->map.end(); ++it) {
|
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;
|
TLSEntry &entry = it->second;
|
||||||
entry.destructFunctor(entry.data);
|
entry.destructFunctor(entry.data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,8 +99,8 @@ void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const {
|
||||||
);
|
);
|
||||||
|
|
||||||
Point2i sourceOffset(
|
Point2i sourceOffset(
|
||||||
glyph.tx.x * m_bitmap->getWidth(),
|
(int) (glyph.tx.x * m_bitmap->getWidth()),
|
||||||
glyph.tx.y * m_bitmap->getHeight());
|
(int) (glyph.tx.y * m_bitmap->getHeight()));
|
||||||
|
|
||||||
dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size);
|
dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size);
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ Vector2i Font::getSize(const std::string &text) const {
|
||||||
if (character == '\r')
|
if (character == '\r')
|
||||||
continue;
|
continue;
|
||||||
if (character == '\n') {
|
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);
|
size.x = std::max(size.x, pos);
|
||||||
pos = 0;
|
pos = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -206,7 +206,7 @@ struct path_to_python_str {
|
||||||
|
|
||||||
|
|
||||||
struct TSpectrum_to_Spectrum {
|
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());
|
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() {
|
void export_core() {
|
||||||
bp::to_python_converter<fs::path, path_to_python_str>();
|
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::implicitly_convertible<std::string, fs::path>();
|
||||||
|
|
||||||
bp::object coreModule(
|
bp::object coreModule(
|
||||||
|
|
|
@ -171,7 +171,6 @@ static void readHelper(Stream *stream, bool fileDoublePrecision,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TriMesh::loadCompressed(Stream *_stream, int index) {
|
void TriMesh::loadCompressed(Stream *_stream, int index) {
|
||||||
ref<Stream> stream = _stream;
|
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, "
|
Log(EError, "Tried to unserialize a shape from a stream, "
|
||||||
"which was not previously set to little endian byte order!");
|
"which was not previously set to little endian byte order!");
|
||||||
|
|
||||||
short format = stream->readShort();
|
const short version = readHeader(stream);
|
||||||
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!");
|
|
||||||
|
|
||||||
if (index != 0) {
|
if (index != 0) {
|
||||||
size_t streamSize = stream->getSize();
|
const size_t offset = readOffset(stream, version, index);
|
||||||
|
stream->seek(offset);
|
||||||
/* Determine the position of the requested substream. This
|
stream->skip(sizeof(short) * 2); // Skip the header
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = new ZStream(stream);
|
stream = new ZStream(stream);
|
||||||
|
@ -282,6 +250,92 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
|
||||||
m_flipNormals = false;
|
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 triange
|
||||||
|
|
||||||
|
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] = static_cast<size_t>(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] = stream->readUInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
} else {
|
||||||
|
Log(EDebug, "The serialized mesh does not contain a valid dictionary");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TriMesh::~TriMesh() {
|
TriMesh::~TriMesh() {
|
||||||
if (m_positions)
|
if (m_positions)
|
||||||
delete[] m_positions;
|
delete[] m_positions;
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#if !defined(__MAXEXP_H)
|
#if !defined(__MAXEXP_H)
|
||||||
#define __MAXEXP_H
|
#define __MAXEXP_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# include <functional>
|
||||||
|
#endif
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class MaxExpDist {
|
class MaxExpDist {
|
||||||
|
|
|
@ -1486,7 +1486,7 @@ void MainWindow::onExportDialogClose(int reason) {
|
||||||
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
|
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
|
||||||
m_currentChild = NULL;
|
m_currentChild = NULL;
|
||||||
|
|
||||||
if (reason == QDialog::Accepted) {
|
if (reason == QDialog::Accepted) {
|
||||||
QString fileName = dialog->selectedFiles().value(0);
|
QString fileName = dialog->selectedFiles().value(0);
|
||||||
settings.setValue("fileDialogState", dialog->saveState());
|
settings.setValue("fileDialogState", dialog->saveState());
|
||||||
exportImage(fileName);
|
exportImage(fileName);
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
#include <mitsuba/core/fstream.h>
|
#include <mitsuba/core/fstream.h>
|
||||||
#include <mitsuba/core/fresolver.h>
|
#include <mitsuba/core/fresolver.h>
|
||||||
#include <mitsuba/core/timer.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
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
@ -152,9 +158,8 @@ public:
|
||||||
|
|
||||||
/* Load the geometry */
|
/* Load the geometry */
|
||||||
Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.filename().string().c_str());
|
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();
|
ref<Timer> timer = new Timer();
|
||||||
loadCompressed(stream, shapeIndex);
|
loadCompressed(filePath, shapeIndex);
|
||||||
Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)",
|
Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)",
|
||||||
m_triangleCount, m_vertexCount, timer->getMilliseconds());
|
m_triangleCount, m_vertexCount, timer->getMilliseconds());
|
||||||
|
|
||||||
|
@ -202,8 +207,88 @@ public:
|
||||||
: TriMesh(stream, manager) { }
|
: TriMesh(stream, manager) { }
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
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&>) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_IMPLEMENT_CLASS_S(SerializedMesh, false, TriMesh)
|
||||||
MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader");
|
MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader");
|
||||||
MTS_NAMESPACE_END
|
MTS_NAMESPACE_END
|
||||||
|
|
|
@ -218,7 +218,7 @@ void blueNoisePointSet(const Scene *scene, const std::vector<Shape *> &shapes,
|
||||||
Cell &cell = cells[cellID];
|
Cell &cell = cells[cellID];
|
||||||
int arrayIndex = cell.firstIndex + trial;
|
int arrayIndex = cell.firstIndex + trial;
|
||||||
|
|
||||||
if (arrayIndex > (int) samples.size() ||
|
if (arrayIndex >= (int) samples.size() ||
|
||||||
samples[arrayIndex].cellID != cellID ||
|
samples[arrayIndex].cellID != cellID ||
|
||||||
cell.sample != -1)
|
cell.sample != -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in New Issue