merge with Edgar's latest changes
commit
82255028d6
|
@ -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);
|
||||
|
|
|
@ -310,6 +310,31 @@ 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})
|
||||
|
|
|
@ -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;
|
||||
|
@ -108,19 +139,24 @@ struct ThreadLocalBase::ThreadLocalPrivate {
|
|||
#else
|
||||
PerThreadData *ptd = ptdLocal;
|
||||
#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) */
|
||||
boost::lock_guard<boost::mutex> guard(ptd->mutex);
|
||||
TLSEntry &entry = ptd->map[this];
|
||||
|
||||
if (EXPECT_NOT_TAKEN(!entry.data)) {
|
||||
/* This is the first access from this thread */
|
||||
entry.data = constructFunctor();
|
||||
void *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 {
|
||||
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 +227,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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,92 @@ 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 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() {
|
||||
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 {
|
||||
|
|
|
@ -1486,7 +1486,7 @@ void MainWindow::onExportDialogClose(int reason) {
|
|||
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
|
||||
m_currentChild = NULL;
|
||||
|
||||
if (reason == QDialog::Accepted) {
|
||||
if (reason == QDialog::Accepted) {
|
||||
QString fileName = dialog->selectedFiles().value(0);
|
||||
settings.setValue("fileDialogState", dialog->saveState());
|
||||
exportImage(fileName);
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
#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 +158,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 +207,88 @@ 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&>) {}
|
||||
};
|
||||
|
||||
/// 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_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